VB6 -  P2P - Peer to Peer Networking com o controle Winsock

Na certa você já ouviu o termo - Peer to Peer - (daqui pra frente apenas P2P). Já ouviu falar no Napster e mais recentemente no Kazaa ???? Pois é , esses e muitos outros programas funcionam baseados no P2P , que nada mais é que um modelo de rede PC para PC onde você faz a comunicação entre dois PC´s em uma rede TCP/IP. (A internet é uma rede TCP/IP).

Como na internet existem milhões de PC´s que podem fazer o papel de cliente como de servidor temos o cenário ideal para explorar o modelo P2P e , neste artigo eu vou mostrar como usar o controle Winsock do Visual Basic para realizar a comunicação entre dois PC´s em uma rede TCP/IP.

Para você saber mais sobre o controle Winsock leia os artigos já publicados neste site em :

Como já falei das propriedades do controle Winsock nos artigos acima vou direto ao que interessa : a aplicação P2P.

Criando seu aplicativo P2P

Teremos na verdade dois aplicativos e por consequência dois projetos : um que fará o papel de cliente e outro que fará o papel de servidor.Nosso objetivo é trocar arquivos entre os dois PC´s semelhante ao que você faz quando baixa uma música usando o Kazaa...

O projeto Servidor

Antes de qualquer iniciativa você deve incluir no seu projeto o componente Winsock. No menu Project|Components selecione : Microsoft Winsock Control X.0 (onde x é a versão do seu controle. Eu estou usando a 6.0)

No evento General Declarations do formulário padrão vamos inserir o seguinte código :

Option Explicit
Dim lPos As Long
Dim bOK As Boolean
Dim fnome As String
Ne definimos as variáveis que serão visivéis no formulário

Crie um novo projeto no Visual Basic do tipo Standard EXE e no formulário padrão insira os controles conforme layout abaixo:

Chamei o projeto de - p2pServidor.vbp , o formulário de frmservidor e dei o nome de wsTCP ao controle Winsock inserido no formulário.

- Estou usando os DriveListBox - Dir1 , DirListBox - Dir1 e FileListBox - File1 . Para exibir os drives , diretórios e arquivos do sistema. O sincronismo entre os controles é feito pelo código abaixo associado ao evento Change de cada controle.

Private Sub Drive1_Change()
      Dir1.Path = Drive1.Drive & "\"
End Sub

Private Sub Dir1_Change()
   File1.Path = Dir1.Path
End Sub

Nota : Se você executar o projeto agora e a seguir executar o comando Netstat -a vai obter:

-Perceba que a estamos usando o protocolo TCP e que a porta 2000 no host - macorati tem seu estado em LISTENING.

No evento Click do botão de comando - Ativar Servidor- insira o seguinte código:

Private Sub cmdAtivar_Click()

If cmdAtivar.Caption = "Ativar Servidor" Then
     cmdAtivar.Caption = "Parar Servidor"
      wsTCP(0).LocalPort = 2000
      wsTCP(0).Listen
Else
     wsTCP(0).Close
     cmdAtivar.Caption = "Ativar Servidor"
End If

End Sub
- O código apenas define a porta na qual o servidor vai estar escutando

Código do evento ConnectionRequest do controle Winsock no formulário - frmservidor:

Private Sub wsTCP_ConnectionRequest(Index As Integer, ByVal requestID As Long)
     Load wsTCP(1)
     If wsTCP(1).State <> sckClosed Then wsTCP(1).Close
     wsTCP(1).Accept requestID
End Sub

- Verificamos o estado do controle, se ele não estiver fechado devemos fechá-lo.

Os principais valores possíveis para a propriedade State são:

Constante Valor Status da conexão
sckClosed 0 Fechada
sckOpen 1 Aberta
sckListening 2 Escutando
sckConnectionPending 3 Pendente
sckConnecting 6 Conectando
sckConnected 7 Conectado
sckClosing 8 Fechando
sckError 9 Erro

- O Método Accept aceita a conexão

Quando os dados estiverem chegando o evento DataArrival é disparado , nele inserimos o seguinte código:

Private Sub wsTCP_DataArrival(Index As Integer, ByVal bytesTotal As Long)

If Not bOK Then
    wsTCP(1).GetData fnome
    If InStr(fnome, vbCrLf) <> 0 Then fnome = Left(fnome, InStr(fnome, vbCrLf) - 1)
    bOK = True
      If Dir(Dir1.Path & "\" & fnome) <> "" Then Kill Dir1.Path & "\" & fnome
      Open Dir1.Path & "\" & fnome For Binary As 1
      lPos = 1
      wsTCP(1).SendData "OK" & vbCrLf
Else
     Dim buffer() As Byte
     wsTCP(1).GetData buffer
     Put #1, lPos, buffer
     lPos = lPos + UBound(buffer) + 1
End If

End Sub
- O método GetData Efetua a recepção do arquivo fnome.

- Extraimos o nome do arquivo e colocamos os dados em um buffer.

Para encerrar a conexão usamos o código abaixo no evento Close:

Private Sub wsTCP_Close(Index As Integer)
  Close #1
   Unload wsTCP(1)
   bOK = False
End Sub

O projeto Cliente

Para cria o projeto cliente siga as mesmas etapas iniciais usadas no projeto servidor:

O nome do projeto cliente será : p2pCliente.vbp e o formulário será chamado de frmcliente.

- Além do controle winsock estou usando o controle CommonDialog - dlg - para buscar um arquivo para ser enviado.

Na seção General Declarations do forma declaramos as variáveis que serão visíveis no formulário:

Option Explicit
Dim buffer() As Byte
Dim lBytes As Long
Dim temp As String

O usuário deverá selecionar um arquivo para enviar para o servidor clicando no botão - cmdBusca - cujo código é o seguinte:

Private Sub cmdBusca_Click()
  dlg.ShowOpen
  txtArquivo.Text = dlg.FileName
End Sub

Após a seleção basta clicar no botão - Enviar Arquivo para o Servidor . O seu código é dado abaixo:

Private Sub cmdEnvia_Click()
   cmdEnvia.Enabled = False
   lBytes = 0

   ReDim buffer(FileLen(dlg.FileName) - 1)

   Open dlg.FileName For Binary As 1
   Get #1, 1, buffer
   Close #1  

   Load wsTCP(1)

   wsTCP(1).RemoteHost = "127.0.0.1"
   wsTCP(1).RemotePort = 2000
   wsTCP(1).Connect

   lblStatus.Caption = "Conectando..."
End Sub
- Criamos um buffer do tamanho do arquivo

- abrimos o arquivo selecionado no modo binário

- Pegamos os dados do buffer

- Definimos o computador para onde iremos enviar o arquivo. Como eu estou na máquina local usamos o número
IP padrão para o computador local - 127.0.0.1

- Definimos a porta para conexão ( deve ser a mesma na qual o servidor esta escutando)

- Efetuamos a conexão

- no evento Connect - indicamos a conexão e enviamos os dados Private Sub wsTCP_Connect(Index As Integer)
     lblStatus = "Conectado !"
     wsTCP(1).SendData dlg.FileTitle & vbCrLf
End Sub

No evento DataArrival - temos o seguinte código:

Private Sub wsTCP_DataArrival(Index As Integer, ByVal bytesTotal As Long)
   wsTCP(1).GetData temp
   If InStr(temp, vbCrLf) <> 0 Then temp = Left(temp, InStr(temp, vbCrLf) - 1)
       If temp = "OK" Then
      wsTCP(1).SendData buffer
   Else
      lblStatus.Caption = "Ocorreu um problema durante a recepção..."
     Unload wsTCP(1)
     cmdEnvia.Enabled = True
End If
End Sub

No evento SendProgress - indicamos a progressão da remessa do arquivo:

Private Sub wsTCP_SendProgress(Index As Integer, ByVal bytesSent As Long, ByVal bytesRemaining As Long)
If temp = "OK" Then
lBytes = lBytes + bytesSent
lblStatus = lBytes & " de um total de " & UBound(buffer) & " bytes enviados"
End If
End Sub

No evento SendComplete - Informamos que a remessa de dados terminou e habilitamos o botão para uma nova transmissão:

Private Sub wsTCP_SendComplete(Index As Integer)
If temp = "OK" Then
   lblStatus.Caption = "Remessa do arquivo completada com sucesso !"
   temp = ""
   Unload wsTCP(1)
   cmdEnvia.Enabled = True
End If
End Sub

Para encerrar a conexão usamos o evento Close:

Private Sub wsTCP_Close(Index As Integer)
lblStatus.Caption = "Conexão fechada..."
Unload wsTCP(1)
End Sub

Agora só falta testar o projeto . Para isto carregue o projeto p2pServidor.vbp em uma instância e o projeto p2pCliente.vbp em outra instância:

- Inicie o projeto p2pServidor.vbp e clique no botão para ativar o servidor . A seguir selecione um diretório para onde deseja receber os arquivos que serão transferidos.

- A seguir inicie o projeto p2pCliente.vbp e selecione um arquivo e depois clique no botão - Enviar Arquivo para o Servidor.

Na figura acima temos uma ilustração do projeto em execução. Se o teste for feito em uma máquina local a transferência deve ser bem rápida.

Pronto ! Você já tem o esquelo de seu projeto P2P cabe a você incrementar a interface e os recursos (e o código) ...


José Carlos Macoratti