|
||
|
|
| Conheça o Plugfeed | » Início » Programação » Visual Basic » Sockets II --> |
|
Avaliação: Não avaliado | Publicado em: 18/02/2008
Sockets II
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 II
Continuando nosso tutorial sobre sockets, vamos agora ver algo mais prático e não tão trivial. A idéia é transmitir um arquivo através da conexão. Como já vimos anteriormente, só é possível transmitir bytes através de um socket, sem qualquer classificação. Isso implica que o socket não sabe (nem se importa) se você quer passar uma "mensagem", um "arquivo" ou um "registro", por exemplo. Você não pode meramente fazer: winsock1.SendData "c:autoexec.bat". Ele não enviará o conteúdo do arquivo "autoexec.bat" que está na raíz da unidade "c:", e sim a string "c:autoexec.bat". Esses "tipos de dados" são abstrações que nós possuímos. Portanto, deveremos criar "regras" a fim de que possamos enviar e receber, corretamente, esses "tipos de dados". A primeira vista isso pode até parecer ruim, no entanto, a meu ver, dá uma grande flexibilidade ao programador permitindo-lhe tratar as informações como bem lhe convir. Pois bem, como definir um "arquivo" (nesse caso)? Existem centenas maneiras diferentes de se fazer isso, e aqui estarei demonstrando apenas uma delas. Essa maneira a qual me refiro é colocar uma determinada informação no começo e no fim da transmissão do arquivo. A idéia disso é simples e vem a seguir: a informação do começo dirá para o outro computador que um arquivo será enviado, a fim de que este se prepare para recebê-lo (abrindo um arquivo no disco para gravação, por exemplo); a informação do fim da transmissão dirá para o outro computador que a transmissão do arquivo acabou (para que ele descarregue os dados do buffer para o arquivo no disco, por exemplo). Quanto ao que seriam essas "informações", por exemplo, *poderíamos* colocar as strings "início" e "fim" para indicá-las. Mas, por que eu disse "poderíamos"? É que aí entra um problema grave. Essas informações ("início" e "fim") trafegarão junto com o conteúdo do arquivo. Provavelmente você já deve ter imaginado o que isso acarreta e que pode ser traduzido pela pergunta a seguir: "e, se no conteúdo do arquivo existirem as palavras 'início' e 'fim'?". Baseando-se somente nessa perspectiva, talvez não haja problema se a string "início" estiver no conteúdo do arquivo, dependendo de como a rotina foi estruturada. Porém, a string "fim", quase com certeza, gerará problemas (já que a rotina está esperando por ela para finalizar o arquivo). Agora que já pensamos em um empeçilho existente nessa maneira, cabe a nós desenvolvermos uma solução para o mesmo. Aqui também haverá uma diversidade muito grande de opções, no entanto, a idéia principal é usar strings que dificilmente serão achadas dispostas de uma tal maneira. Digo "dificilmente" porque é possível achá-las, mas é extremamente improvável. A fim de especificarmos também o nome do arquivo, podemos usar "", onde em "arquivo.ext" estará o nome do arquivo. Para dificultar ainda mais a possibilidade de uma string assim ser encontrada no conteúdo do arquivo podemos acrescentar outros caracteres ao começo e fim da mesma, como chr(0) e chr(255), por exemplo. Para o caso do fim, é interessante fazer algo semelhante, colocando o tamanho do arquivo no lugar do nome, ficando "", a fim de podermos validar se o arquivo foi enviado corretamente (pelo menos o número de bytes do mesmo). É claro que "arquivo.ext" e "32442" irão variar de acordo com o arquivo que está sendo enviado. Por isso, deveremos procurar por "" que fecha a sequência a fim de que saibamos o nome do arquivo. O mesmo vale para o fim, ou seja, procuraremos por "" que fecha a sequência. Para ficar melhor ainda, adicionaremos também os caracteres chr(0) e chr(255) numa ordem arbitrária. Essa ordem será "chr(0) & chr(255) & chr(0)" e estará antes de cada "<" e depois de cada ">". Bem, chega de teoria e vamos à prática. Abra os mesmos projetos do tutorial anterior (prjServidor e prjCliente). A parte de conexão já está toda pronta, por isso não precisaremos fazer novamente. Focaremo-nos apenas no envio e recepção de arquivos. Coloque uma textbox em cada form e nomei-a "txtArquivo". Vá na opção Components do menu Project e marque o Microsoft Common Dialog Control. Em seguida, insira o mesmo em cada form, e para encurtar o nome, nomei-o como "CD1". Repare que tudo que é feito no projeto Servidor deverá ser feito no projeto Cliente também. É claro que poderia ser apenas um e servir aos dois propósitos, mas aí fica a seu critério e você deverá tomar as medidas necessárias para que o projeto funcione dos dois modos. Insira dois botões, nomeando-os "cmdProcurar" e "cmdEnviarArquivo". Não estou explicitando, mas a propriedade Caption de cada botão fica óbvia pelos nomes. O código a seguir ficará no evento Click do botão cmdProcurar (para ambos os projetos): CÓDIGO CD1.CancelError = True On Error Resume Next CD1.ShowOpen If Not Err.Number = cdlCancel Then txtArquivo.Text = CD1.FileName End If On Error GoTo 0 Essa rotina permitirá que escolhamos o arquivo e o caminho do mesmo será mostrado na textbox txtArquivo. O código a seguir ficará no evento Click do botão cmdEnviarArquivo (para ambos os projetos, observe apenas a linha antes do "End If", que deverá ser diferente de acordo com o projeto): CÓDIGO Dim início As String, delim As String, fim As String Dim tamanho As Double Dim n As Byte Dim nome As String, paraEnviar As String início = Chr(0) & Chr(255) & Chr(0) & "<início:" fim = Chr(0) & Chr(255) & Chr(0) & "<fim:" delim = ">" & Chr(0) & Chr(255) & Chr(0) If Dir(txtArquivo.Text) <> "" And trim(txtArquivo.Text) <> "" Then n = FreeFile() Open txtArquivo.Text For Binary As #n tamanho = LOF(n) conteúdo = Input(tamanho, n) Close #n nome = Right(txtArquivo.Text, Len(txtArquivo.Text) - InStrRev(txtArquivo.Text, "")) paraEnviar = início & nome & delim & conteúdo & fim & tamanho & delim sckServidor.SendData paraEnviar '** sckCliente.SendData paraEnviar End If Essa rotina formatará os dados da forma como já foi mostrada anteriormente e enviará para o outro computador através do socket. Falta agora apenas a parte da recepção, que se dará no evento DataArrival do socket (lembra-se do DataArrival no primeiro tutorial, certo?). O código que deverá ficar nesse evento será (note que deixei o código do projeto anterior também, apesar de não ser muito útil mostrar os dados na listbox): CÓDIGO Static buffer As String Static n As Byte Static tamanho As Double Static nome As String Dim dados As String sckServidor.GetData dados '** sckCliente.GetData dados lbStatusSvr.AddItem "Recebido: " & dados '** lbStatusClt.AddItem "Recebido: " & dados buffer = buffer & dados Dim início As String, delim As String, fim As String Dim iniciar As Boolean, finalizar As Boolean início = Chr(0) & Chr(255) & Chr(0) & "<início:" fim = Chr(0) & Chr(255) & Chr(0) & "<fim:" delim = ">" & Chr(0) & Chr(255) & Chr(0) pos = InStr(1, buffer, início) iniciar = False If pos > 0 Then pos2 = InStr(pos, buffer, delim) If pos2 > 0 Then nome = Mid(buffer, pos + Len(início), pos2 - pos - Len(início)) buffer = Mid(buffer, pos2 + Len(delim), Len(buffer) - pos2 - Len(delim) + 1) iniciar = True End If End If finalizar = False pos = InStr(1, buffer, fim) If pos > 0 Then pos2 = InStr(pos, buffer, delim) If pos2 > 0 Then tamanho = CDbl(Mid(buffer, pos + Len(fim), pos2 - pos - Len(fim))) buffer = Mid(buffer, 1, pos - 1) finalizar = True End If End If If iniciar Then n = FreeFile() Open "c:" & nome For Binary As #n End If If finalizar Then If Len(buffer) = tamanho Then Put #n, , buffer Else MsgBox "Tamanho original do arquivo difere da quantidade recebida de bytes!" End If Close #n MsgBox "Concluído!" End If A rotina acima faz o seguinte: a cada vez que os dados são recebidos, os mesmos são acumulados na variável buffer e também é verificado se trata-se do envio de um arquivo (quando começa) ou se trata-se do fim do arquivo (quando termina). Ao término ele verifica se a quantidade recebida de bytes está correta e grava o arquivo no disco (nesse caso, grava na raíz da unidade C: com o nome original do arquivo). Para quem não está muito familiarizado com algumas funções do VB, seguem agora alguns esclarecimentos. InStr() procura uma substring, ou seja, a posição de uma string em outra (sempre começando a partir de 1, e retorna 0 caso não encontre). Len() retorna o tamanho de uma string. Mid() retorna uma substring, ou seja, um pedaço de uma string. Agora execute ambos os projetos, clique no botão Aguardar no projeto Servidor e clique no botão Conectar do projeto Cliente. Escolha um arquivo em qualquer um dos dois pelo botão "Procurar" e clique no botão "Enviar arquivo". Devo lembrar que é essa só uma das possíveis maneiras de fazer isso, e nem mesmo se trata da melhor delas. Para ter uma idéia, o exemplo desse tutorial não é o ideal se você quer passar arquivos maiores do que 3,5MB; outro fator negativo é que a leitura de arquivos desse tamanho são demorados (se lidos de uma vez só, como é o caso), além de consumir muita memória; não há qualquer resposta do receptor para o transmissor indicando-lhe que o mesmo pode enviar mais dados. Em suma, carece de diversos cuidados técnicos que devem ser imagininados pelo programador. Portanto, trata-se apenas de uma experiência deveras simplificada com o único propósito de fixar a idéia de como funciona a comunicação através de sockets. Espero que tenham gostado desse segundo 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 2 de 2 da seguinte série:
![]() |
|
![]() |
|
|