Comunicação via socket com C#

Postado por Carlos Fernando Sylverio | Postado em Programação | Postado em 22-12-2009

0

Introdução

Primeiramente vamos conceiturar o que é socket, ou soquete em portugues. De uma visão geral um soquete pode ser definido como uma tomada que designa uma cavidade ou região usada para ligar algum artifício específico.

No mundo da computação, um socket é o elo de ligação entre os processos do servidor e do cliente. Ele é a “porta” na qual os processos enviam e recebem mensagens. De acordo com JAMES F KUROSE: “socket é a interface entre a camada de aplicação e a de transporte dentro de uma máquina”. Para quem não lembra, ou não sabe, camada de aplicação e transporte fazem parte do modelo OSI.
Através de um socket podemos estabelecer a comunicação entre máquinas possibilitando o envio e recebimento de dados.

A interface padronizada de sockets surgiu originalmente no sistema operacional Unix BSD (Berkeley Software Distribution). Tinha a função de suporte a comunicação em rede. Esta interface é a base para a maioria das interfaces entre protocolos de internet TCP/IP existente.

A identificação de um socket na rede é realizada por um IP e um porta. Comumente utiliza-se portas acima de 1000 pois as inferiores são utilizadas pelo sistema operacional. Sua comunicação é realizada pelos protocolos UDP ou TCP. Assim, é possível termos tanto comunicação orientada a conexão (via TCP), quanta não orientada a conexão (via UDP). O socket abstrai esse conceito, permitindo assim a utilização de qualquer um dos meios.

No C# para se trabalhar com sockets os recursos enconstram-se no namespace System.Net.Sockets.

Implementando uma aplicação com socket

Este é um simples código de uma aplicação Client/Server utilizando socket.
Abaixo o passo a passo de criação da aplicação:

Server App

  1. Criar um projeto do tipo WindowsForm com o nome CommunicationSocket.
  2. Incluir um botão que será utilizado para enviar mensagem do servidor para o cliente.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
 
namespace CommunicationSocket
{
    public partial class ServerApp : Form
    {
        private Socket socket;
        private Thread thread;
 
        private NetworkStream networkStream;
        private BinaryWriter binaryWriter;
        private BinaryReader binaryReader;
 
        public ServerApp()
        {
            InitializeComponent();
            thread = new Thread(new ThreadStart(RunServer));
            thread.Start();
        }
 
        public void RunServer()
        {
            TcpListener tcpListener;
            try
            {
                IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2001);
                tcpListener = new TcpListener(ipEndPoint);
                tcpListener.Start();
 
                MessageBox.Show("Servidor habilitado e escutando porta...", "Server App");
 
                socket = tcpListener.AcceptSocket();
                networkStream = new NetworkStream(socket);
                binaryWriter = new BinaryWriter(networkStream);
                binaryReader = new BinaryReader(networkStream);
 
                MessageBox.Show("conexão recebida!", "Server App");
                binaryWriter.Write("\nconexão efetuada!");
 
                string messageReceived = "";
                do 
                {
                    messageReceived = binaryReader.ReadString();
                    MessageBox.Show("Mensagem: " + messageReceived, "Server App");
 
                } while (socket.Connected);
            } 
            catch (Exception ex) 
            {
                MessageBox.Show(ex.Message);
            } 
            finally 
            {
                binaryReader.Close();
                binaryWriter.Close();
                networkStream.Close();
                socket.Close();
 
                MessageBox.Show("conexão finalizada", "Server App");
            }
        }
 
        private void btnSendMsg_Click(object sender, EventArgs e)
        {
            try
            {
                binaryWriter.Write("Server respondendo: Houston, we have a problem!!!");
            }
            catch (SocketException socketEx)
            {
                MessageBox.Show(socketEx.Message, "Erro");
            }
        }
    }
}

Client App

  1. Criar um projeto do tipo WindowsForm com o nome ClientApp.
  2. Incluir botão que será utilizado para enviar mensagem do cliente para o servidor.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
using System;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
 
namespace ClientApplication 
{
    public partial class ClientAppForm : Form 
    {
        private NetworkStream networkStream;
        private BinaryWriter binaryWriter;
        private BinaryReader binaryReader;
        private TcpClient tcpClient;
 
        private Thread thread;
 
        public ClientAppForm() 
        {
            InitializeComponent();
            thread = new Thread(new ThreadStart(RunClient));
            thread.Start();
        }
 
        public void RunClient() 
        {
            try 
            {
                tcpClient = new TcpClient();
                tcpClient.Connect("127.0.0.1", 2001);
 
                networkStream = tcpClient.GetStream();
                binaryWriter = new BinaryWriter(networkStream);
                binaryReader = new BinaryReader(networkStream);
 
                String message = "";
                do 
                {
                    try 
                    {
                        message = binaryReader.ReadString();
                        MessageBox.Show(message, "Mensagem Recebida");
                    } 
                    catch (Exception ex) 
                    {
                        MessageBox.Show(ex.Message, "Erro");
                        message = "FIM";
                    }
                } while (message != "FIM");
 
                binaryWriter.Close();
                binaryReader.Close();
                networkStream.Close();
                tcpClient.Close();
            } 
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Erro");
            }
        }
 
        private void btnSendMsg_Click(object sender, EventArgs e) 
        {
            try 
            {
                binaryWriter.Write("Mensagem do cliente");
            } 
            catch (SocketException socketEx) 
            {
                MessageBox.Show(socketEx.Message, "Erro");
            }
        }
    }
}

Para realizar a comunicação o Servidor utiliza o objeto TcpListener que fica escutando toda requisição no IP “127.0.0.1″ porta 2001.
Na aplicação Cliente o objeto TcpClient é informado sobre o IP (ou DNS) e porta do servidor que irá se conectar. Após esse a realização toda a comunicação é feita por meio de stream através do objeto NetworkStream.

Repare que tanto Servidor quanto o Cliente trabalha com processamento em paralelo (Thread) para evitar que a aplicação fique travada até o término do processamento, ou para manter um processamento dedicado e contínuo que é o caso do servidor.

Nesse exemplo utilizei MessageBox para apresentar as mensagens, pois para inseri-las em um TextBox no Form é necessário utilizar delegates e reflections, senão teremos um erro de cruzamento de Threads (Cross-thread operation not valid).
Mas isso acho que é assunto para o nosso próximo post.

Até mais ;-)

Extension Methods no C# 3.0

Postado por Carlos Fernando Sylverio | Postado em Programação, Tecnologia | Postado em 13-12-2009

2

Extension Methods é uma das muitas características que torna o LINQ possível .
Em resumo podemos dizer que Extension Methods é a possibilidade de inserir métodos em objetos já compilados. O método criado tem a característica de um método estático, porem só está acessível ao objeto associado.
Com esse recurso podemos adicionar aos .NET types novos métodos. Assim classes como StringHelpers, Util, entre outras, com uma variedade de métodos de auxílio, podem ser inseridos diretamente no fonte.
Exemplo de uma classe com validação:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System.Text.RegularExpressions; 
namespace ExtensionMethodsExample
{
    public class Util
    {
        /// <summary>Verifica se o argumento do tipo string é um numérico</summary>
        /// <param name="arg">argumento a ser validado</param>
        /// <returns>True - se for numérico</returns>
        public bool IsNumeric(string arg)
        {
            return Regex.IsMatch(arg, @"^\d+$");
        }
    }
}

Com o uso do Extension Methods podemos inserir o método que verifica se a variável é numérica no próprio tipo string.
Abaixo um exemplo de seu uso, e depois vou explicar como o extension methods é declarado.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using System;
using System.Text.RegularExpressions;
 
namespace ExtensionMethodsExample
{
    public static class Extensions
    {
        public static bool IsNumeric(this string arg)
        {
             return Regex.IsMatch(arg, @"^\d+$");
        }
    }
} 
 
public class Program
{
    public static void Main(string[] args)
    {
        string codigo1 = "1234";
        string codigo2 = "ABCD";
 
        Imprime(codigo1);
        Imprime(codigo2);
    }
 
    private static void Imprime(string codigo)
    {
        if (codigo.IsNumeric())
        {
            Console.WriteLine("Codigo numerico: {0}", codigo);
        }
        else
        {
            Console.WriteLine("Codigo alphanumerico: {0}", codigo);
        }
    }
}

Para criarmos um extension methods precisamos de uma classe e método estático. Todo método extension sempre terá seu primeiro parâmetro a palavra reservada this, seguido do tipo ao qual o método será inserido, no exemplo o tipo string. Esse parâmetro representa a própria instância do objeto. Para os parâmetros subseqüentes serão utilizados na chamada do método. O nome da classe é de livre escolha, ela não interfere na implementação do extension methods, a única ressalva é que seja uma classe estática.

Compare a diferença da verificação de condição utilizando a classe Util e o extension methods

1
2
3
4
5
Util util = new Util();
if (util.IsNumeric(codigo))
{
    // faz algo
}

e

1
2
3
4
if (codigo.IsNumeric())
{
    // faz algo
}

O código torna-se mais legível e mais implícito seus métodos, tornando a linguagem muito mais dinâmica.
Enjoy ;-)

Entendento o SQL Server sysobjects e syscolumns

Postado por Carlos Fernando Sylverio | Postado em Programação, Tecnologia | Postado em 07-12-2009

2

SQL Server sysobjects é uma tabela do SGBD SQL Server que armazena informações sobre os objetos criado no banco de dados. Contendo uma linha (registro) para cada objeto existente, como uma restrição, padrão, log, regra e procedimento armazenado.
No exemplo abaixo podemos encontrar informação sobre todos os objetos utilizados no banco de dados:
 

SELECT * FROM sysobjects
 

A coluna type e xtype possuem informação sobre o tipo de objeto. Tome cuidado para não confundir os valores das constantes. Há pequenas particularidades entre elas.
Para exibir todas as tabelas criadas pelo usuário executamos a seguinte instrução:
 

SELECT  * FROM sysobjects WHERE xtype='U'
 

Ou saber as trigger existentes em cada tabela:

 
SELECT
    Sys2.name           AS TableName,
    Sys1.name           AS TriggerName,
    CASE
        WHEN Sys1.deltrig > 0 THEN 'Delete'
        WHEN Sys1.instrig > 0 THEN 'Insert'
        WHEN Sys1.updtrig > 0 THEN 'Update'
    END
FROM
    sysobjects Sys1
    INNER JOIN sysobjects Sys2 ON
        Sys1.parent_obj = Sys2.id
WHERE
    Sys1.xtype = 'TR'
ORDER BY
    Sys2.name
 

Lista de possibilidades de valores para a coluna xtype:

  • C = CHECK constraint 
  • D = Default or DEFAULT constraint 
  • F = FOREIGN KEY constraint 
  • L = Log 
  • P = Stored procedure 
  • PK = PRIMARY KEY constraint (type is K) 
  • RF = Replication filter stored procedure 
  • S = System table 
  • TR = Trigger 
  • U = User table 
  • UQ = UNIQUE constraint (type is K) 
  • V = View 
  • X = Extended stored procedure

 
A tabela syscolumns retorna uma linha para cada coluna de um objeto que tem colunas, como exibições ou tabelas. E pode ser um auxilio na obtenção de informação sobre as colunas das tabelas:

 
SELECT 
    *
FROM 
    sysobjects SysObj
    INNER JOIN syscolumns SysCol ON
        SysObj.id = SysCol.id
WHERE 
    SysObj.type='U'
    AND
    SysObj.name = 'tbl_cliente'
 

Referência:

Observação: Os exemplos deste post foram criados para a versão 2005 do SQL Server, para versão 2000 do SQL Server consulte o artigo Mapeando tabelas do sistema do SQL Server 2000 para exibições do sistema do SQL Server 2005

Quando utilizar Literal e Label

Postado por Carlos Fernando Sylverio | Postado em Programação, Tecnologia | Postado em 02-12-2009

0

Primeiramente vamos as definições:

  • Literal é um controle que reserva um local da página para exibir um texto estático.
  • Label é um controle de rótulo usado para mostrar textos na página.

Ambos os controles possuem a propriedade Text, que define o texto a ser exibido.
Não há um controle melhor ou pior. E sim saber quando e como utilizar esses controles corretamente. O mais importante é entender e saber o que é produzido em sua renderização.
É nesse momento que as diferenças ficam mais aparentes.

Criando um projeto para comparar os controles
Para exemplificar os controles criei um ASP.Net Web Site com o nome LabelTeste
Na página Default.aspx inclui o seguinte código interno a tag form:

<div id="teste">
        <h3>Controles ASP.Net</h3>
        <p><asp:Label ID="lblTeste1" runat="server"></asp:Label></p>
        <p><asp:Literal ID="ltrTeste1" runat="server"></asp:Literal></p>
        <p><asp:Label ID="lblTeste2" AssociatedControlID="txtTeste2" runat="server"></asp:Label><asp:TextBox ID="txtTeste2" runat="server"></asp:TextBox></p>
</div>

Ao executar a aplicação, teremos a seguinte tela:

Tela Web Control

Clique com o botão direito e selecione codigo-fonte e veja o código gerado 

<div id="teste">
        <h3>Controles ASP.Net</h3>
        <p><span id="lblTeste1">label 1</span></p>
        <p>literal 1</p>
        <p><label for="txtTeste2" id="lblTeste2">label 2</label><input name="txtTeste2" type="text" id="txtTeste2" /></p>
</div>

Repare no código gerado. O controle Label renderiza o texto interno das tags span, que na maioria das vezes é desnecessário. Isso aumenta a quantidade de código a ser trafego do servidor para o cliente. Neste caso que só queriamos a apresentação de um texto, damos preferência ao Literal, que renderiza somente o texto de uma forma estática sem geração de tags.
A principal vantagem do Label é quando necessitamos de associa-lo a um outro controle. Que é feito por meio do atributo AssociatedControlID.
Assim é gerado as tags label que são utilizadas para dar foco a um outro controle.

Até mais
;-)