Conheça o Plugfeed | » Início » Programação » Visual Basic » Sockets -->
 
Avaliação: | Publicado em: 18/02/2008
Sockets
Fred Jader Desenvolvedor web desde os 12 anos, Frederico Jader hoje em dia atua como diretor de arte e administrador de portais em geral, tendo como mais novo projeto o site www.gamesnahora.com
Sockets

A grosso modo, um socket é uma conexão entre duas portas. E através dessa conexão pode-se enviar ou receber bytes. Essas portas podem ser números arbitrários, como 1984 e 2001, ou números específicos quando você quiser se conectar a um determinado servidor (por exemplo, você usa a porta 110 para se conectar a um servidor POP3 e a porta 25 para se conectar a um servidor SMTP). Além disso, outro requisito para realizar uma conexão é especificar um endereço. Este endereço pode ser o nome de um computador (em uma rede local) ou um endereço IP (de uma rede local ou da Internet). Pode-se também, e será assim nesse tutorial, conectar-se ao próprio computador (nem todo mundo dispõe de dois computadores para realizar estes testes), seja especificando o próprio IP do computador (ou o nome dele) ou especificando um IP especial, chamado loopback (a saber: 127.0.0.1). Sempre que me referir a dois computadores tenha em mente de que ambos são o mesmo computador.
O conceito de realizar uma conexão em socket é que um computador esteja aguardando (escutando) uma conexão em uma determinada porta e que outro computador tente se conectar ao primeiro na determinada porta. Quando uma porta está esperando uma conexão dizemos que ela está "aberta". Chamaremos de "servidor" o computador que está esperando por uma conexão (ou seja, que está com uma porta aberta), e de "cliente" o computador que tentará se conectar. Pois bem, vamos agora a uma aplicação prática desses conceitos.
Mas antes, tenha em mente que este tutorial visa apenas lhe mostrar o básico da utilização de sockets. Nesse caso, só será mostrado como enviar e receber strings entre dois computadores (ou no mesmo computador). Especulações sobre como utilizar isso de forma útil são apresentadas no último parágrafo.

Abra o VB duas vezes, nomeie um dos projetos de "prjServidor" e o outro de "prjCliente". Lembre-se de salvar os projetos a cada modificação que for feita. Adicione, em ambos os projetos, o Microsoft Winsock 6.0 pela opção Components do menu Project. Em seguida coloque um controle Winsock em cada form, e siga a padronagem de nomes, mudando-os para "sckServidor" e "sckCliente" nos respectivos projetos. Em ambos os projetos, coloque também uma listbox e nomeie-a como "lbStatusClt" e "lbStatusSvr", respectivamente. Coloque um botão no projeto cliente, de nome "cmdConectar" (e caption = "Conectar"), e no projeto servidor coloque um botão de nome "cmdAguardar" (e caption="Aguardar").

Vejamos o código necessário para que o servidor fique com uma porta aberta, ou seja, esperando uma conexão (isso deve ser colocado no evento Click do botão cmdAguardar):

CÓDIGO

If sckServidor.State <> sckClosed Then sckServidor.Close
sckServidor.Bind 1984
sckServidor.Listen
lbStatusSvr.AddItem "Escutando porta " & sckServidor.LocalPort


Em primeiro lugar, verificamos se o socket está realmente fechado e se não estiver fechamo-lo. É claro que a primeira que o código fosse executado o socket estaria mesmo fechado, mas da segunda vez não e um erro seria apresentado (pelo fato de tentarmos colocar em listening um socket que está em outro estado). Eis o porquê de sempre fecharmos o socket antes. Na linha seguinte, anexamos (bind) a porta 1984 ao socket e na próxima dizemos para que ele "escute" (listen) na porta anexada. Ele estará então esperando por uma conexão.

Vejamos agora o código necessário para que o cliente tente se conectar a esse servidor (coloque isso no evento Click do botão cmdConectar):

CÓDIGO

If sckCliente.State <> sckClosed Then sckCliente.Close
sckCliente.Connect "127.0.0.1", 1984
Do While sckCliente.State <> sckConnected
DoEvents: DoEvents: DoEvents: DoEvents
If sckCliente.State = sckError Then
lbStatusClt.AddItem "Erro!"
Exit Do
End If
lbStatusClt.AddItem "Conectando..."
Loop
If sckCliente.State = sckConnected Then
lbStatusClt.AddItem "Conectado!"
End If


Primeiro fechamos o socket, se ele já não estiver fechado. Depois conectaremos usando o método Connect e especificando o endereço IP (ou nome) do computador e a porta a qual queremos nos conectar. A propriedade State, como você já deve ter percebido, nos indica o "estado" do socket, ou seja, se ele está conectado, se está conectando, se está fechado, se está fechando, etc. Existem diversas constantes com os mesmos valores que essa propriedade pode assumir a fim de que fique mais claro (é muito mais inteligível a palavra sckConnected do que o valor 7). Voltando ao caso do método Connect, esse método será executado e o código abaixo dele continuará a ser executado, ou seja, o processo de execução *não* ficará parado na linha do Connect até que a conexão seja estabelecida. E essa conexão não é instantânea. Por isso, temos que ficar em um loop até que tenhamos certeza de que a conexão foi estabelecida. A função DoEvents serve para liberar os recursos do sistema operacional (para o programa em discussão) a fim de que o mesmo não fique preso no loop sem realizar outras tarefas. Portanto aquele bloco Do While serve justamente para "prender" a execução do código até o estabelecimento da conexão (ou seja, até que sckCliente.State = sckConnected). Caso ocorra algum erro (ou seja, sckCliente.State = sckError), o laço também será quebrado. Após o loop, verificamos se a conexão foi estabelecida (alguma exceção pode ter ocorrido). Em todos esses casos, estamos preenchendo a listbox a fim de vermos claramente o comportamento do socket.
Você deve estar pensando agora que o fundamental para uma conexão já existe e que já pode tentar executá-lo, certo? Mas ainda falta um elemento crucial nessa história. É necessário que o servidor "aceite" a conexão, por isso que sempre me referia ao ato de conectar como uma "tentativa". O evento Connection_Request sempre é executado quando uma conexão é requerida ao socket. Seu único parâmetro é requestID que é um número de identificação do pedido. Portanto, o código necessário para que o socket aceite uma conexão é (isso deverá entrar no evento Connection_Request do sckServidor):

CÓDIGO

If sckServidor.State <> sckClosed Then sckServidor.Close
sckServidor.Accept requestID
lbStatusSvr.AddItem "Aceitou uma conexão com " & sckServidor.RemoteHostIP & " (" & sckServidor.RemotePort & ")"


Novamente devemos fechar o socket antes de aceitar a conexão. Isso porque devemos tirá-lo do modo listening antes de aceitar a conexão. A segunda linha, obviamente, faz o ato propriamente dito de "aceitar a conexão". A terceira linha coloca na listbox o computador e a porta a qual o servidor está conectado.
Agora sim, você já pode executar o projeto servidor e apertar no botão "Aguardar", executar o projeto cliente e clicar no botão "Conectar", nessa ordem. O projeto cliente deverá mostrar "Conectado!" na última linha da listbox e o projeto servidor deverá mostrar "Aceitou uma conexão com 127.0.0.1 (xxxx)", onde "xxxx" será um número arbitrário.

Se deu tudo certo como descrito acima, façamos agora o envio e recebimento de dados entre os sockets. Coloque uma textbox em cada um dos projetos e nomeie-as como "txtEnviar". Coloque também um botão em cada um dos projetos e nomeie-os como "cmdEnviar" (e caption="Enviar"). No evento Click do botão "cmdEnviar" do projeto Cliente entrará o seguinte código:

CÓDIGO

If sckCliente.State = sckConnected Then
sckCliente.SendData txtEnviar.Text
Else
MsgBox "Socket não conectado!"
End If


Verificamos se o socket está conectado e, caso positivo, utilizamos o método SendData para enviar o conteúdo da textbox txtEnviar. Se o socket não estiver conectado, uma mensagem será mostrada avisando isso.

O mesmo entrará no botão "cmdEnviar" do projeto servidor, porém mudando o nome do controle Winsock, ou seja:

CÓDIGO

If sckServidor.State = sckConnected Then
sckServidor.SendData txtEnviar.Text
Else
MsgBox "Socket não conectado!"
End If


Com isso está pronta a parte de envio. Partamos agora para a parte de recebimento. Todas as vezes que dados chegam no socket o evento DataArrival é executado e ele tem como parâmetro bytesTotal, que serve para nos mostrar quantos bytes (ou caracteres, já que 1 byte = 1 caracter) foram recebidos. Colocaremos o seguinte código no evento DataArrival do sckCliente:

CÓDIGO

Dim dados As String
sckCliente.GetData dados
lbStatusClt.AddItem "Recebido: " & dados


Declaramos a variável "dados" como uma string e utilizamos o método GetData para colocar os dados recebidos na variável. Em seguida, adicionamos o conteúdo da variável na listbox.

De forma análoga, colocaremos o seguinte no evento DataArrival do sckServidor:

CÓDIGO

Dim dados As String
sckServidor.GetData dados
lbStatusSvr.AddItem "Recebido: " & dados


Falamos até agora em "cliente" e "servidor", parecendo que existe alguma diferença crucial entre eles, no entanto, pelo que vimos a única diferença é que um aguarda e o outro tenta conectar com este que está aguardando. Porém, após a conexão ser aceita, por parte do que estava esperando, não existem mais diferenças e ambos podem enviar e/ou receber de forma igual.
Execute agora o projeto servidor e clique em "Aguardar", e execute o projeto cliente e clique em "Conectar". Após a notificação de que o cliente está conectado, digite algo na textbox e clique em "Enviar". Verifique na janela do servidor que o texto estará na listbox. Faça o mesmo ao contrário, ou seja, digitando no servidor e verificando se chega no cliente.

E pronto, creio agora que você saiba pelo menos o mínimo sobre como estabelecer uma conexão entre dois sockets e como enviar e/ou receber dados entre eles. É claro que existirão alguns pormenores que você descobrirá enquanto estiver desbravando esse novo mundo. As possibilidades de uso de sockets são enormes, e podemos citar algumas utilizações práticas. Por exemplo, você pode criar o seu próprio banco de dados cliente/servidor (que funcionará como o SQL Server ou o MySQL). Quando falo "banco de dados cliente/servidor" me refiro a estrutura de envio e recepção dos dados (o BD em si pode ser qualquer um), ou seja, você fará trafegar na rede somente os dados requisitados pelo cliente e não o arquivo todo (claramente: menos tráfego, mais rápido). Da mesma forma, o cliente só enviará para o servidor os dados que deseja incluir ou alterar e o servidor se encarregará de fazê-lo (ou seja, o cliente não terá acesso direto ao arquivo do banco de dados, somente o servidor terá). Mesmo que o desempenho não seja igual ao de outros banco de dados cliente/servidor (mas acredito que será rápido o bastante para competir), você terá a vantagem de que será "grátis" (é claro, não é "grátis" de verdade, já que você teve de gastar alguns neurônios, seus dedos, o teclado, energia, etc.), e você não precisará comprar "licenças" e poderá fazer alterações quando bem entender (de forma a extendê-lo). Outra possibilidade de uso de sockets é para comunicação entre programas e até mesmo entre linguagens de programação. Nesse último caso, se uma determinada rotina é mais fácil (ou mais prática) de se fazer em outra linguagem, a qual você não tem como (ou dá muito trabalho) juntar ao seu projeto, você poderá desenvolver essa rotina na forma de um programa completo e as chamadas à ela e os retornos dela seriam todos controlados através de uma comunicação entre o seu programa principal e esse programa menor. Essa interação pode ir ainda mais longe e comunicar não apenas programas diferentes num mesmo computador, mas também comunicar programas em computadores diferentes e que rodem sistemas operacionais diferentes, por exemplo. Outro uso extensivo de sockets é em programas P2P, como o KazaA, por exemplo, ou em programas para bate-papo. Como tudo tem seu lado negro, os adeptos dos trojans (cavalos-de-tróia), usam sockets na criação de programas que deixam uma porta aberta no computador e que são programados para receber determinados comandos e executá-los na máquina hospedeira. É claro que isso pode ser usado de forma positiva, como um "controle remoto", por exemplo, para você controlar seu computador do trabalho quando está em casa. Outra tarefa, aliás muito procurada pelos programadores VB, e que pode ser implementada com o uso de sockets é o envio e o recebimento de e-mails. Tanto para o envio quanto para o recebimento você deverá conectar-se a um servidor e à uma porta específicos, e após estabelecida a conexão você deverá enviar os comandos que o servidor reconhece. Esses exemplos de uso são apenas alguns devaneios de momento que fui capaz de ter (é assustador o quê o simples envio de strings de um computador para outro podem te fazer conceber... :) ).
Espero que tenham gostado desse pequeno tutorial sobre sockets em VB. Caso haja algo errado (sempre há!) ou você queira fazer sugestões ou críticas (positivas e/ou negativas!) ou mesmo tirar dúvidas, você pode contactar o autor através do endereço: washingtonj@openlink.com.br.
Happy socketing!
Este artigo é a parte 1 de 2 da seguinte série:
  1. Sockets
  2. Sockets II

maravilhoso, dá pra aprender demais.








Um produto Detetive.net