C# - Compartilhando arquivos - Cliente/Servidor (socket) - Servidor

 Neste artigo vamos criar uma aplicação para compartilhar arquivos usando a arquitetura cliente/servidor e os recursos dos sockets na linguagem C#.


Na primeira parte do artigo criamos a aplicação TCPCliente para enviar arquivos e agora vamos criar a aplicação TCPServidor que vai atuar como um servidor para receber o arquivo enviado.

Lembrando que o exemplo funciona para arquivos menores que 1 GB e que para testar em máquinas remotas você deve verificar a comunicação entre as máquinas e as configurações do seu firewall.

Então vamos ao trabalho...

Criando o projeto no VS Community 2015

Abra no VS community 2015 e no menu File clique em New Project;

A seguir selecione o template Visual C# -> Windows -> Windows Forms Application e informe o nome TCPServidor e clique em OK;

Agora abra o formulário Form1.cs e inclua os seguintes controles no formulário:

Disponha os controles conforme o leiaute da figura abaixo:

Definindo o código do formulário

Inclua os seguintes namespaces no formulário:

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

No início do formulário vamos declarar um delegate e um evento:

public delegate void FileRecievedEventHandler(object fonte, string nomeArquivo);
public event FileRecievedEventHandler NovoArquivoRecebido;

No evento Load do formulário inclua o código abaixo estamos definindo o tratamento do evento definido:

    private void Form1_Load(object sender, EventArgs e)
    {
            this.NovoArquivoRecebido += new FileRecievedEventHandler(Form1_NovoArquivoRecebido);
    }

A seguir temos o código que define o evento definido :

    private void Form1_NovoArquivoRecebido(object fonte, string nomeArquivo)
    {
            this.BeginInvoke(
             new Action(
             delegate ()
             {
                 MessageBox.Show("Novo Arquivo recebido\n" + nomeArquivo);
                 System.Diagnostics.Process.Start("explorer", @"c:\");
             }));
   }

No evento Click do botão de comando Estabelecer Conexão inclua o código abaixo:

       private void btnEstabelecerConexao_Click(object sender, EventArgs e)
        {
            int porta = int.Parse(txtPorta.Text);
            string endIP = txtEnderecoIP.Text;
            try
            {
                Task.Factory.StartNew(() => TratamentoArquivoRecebido(porta, endIP));
                MessageBox.Show("Escutando na porta...: " + porta);
            }
            catch(Exception ex)
            {
                MessageBox.Show("Erro : " + ex.Message);
            }
        }

Neste código obtemos os valores para o IP e  porta e chamamos o método TratamentoArquivoRecebido() passando essas informações para fazer o tratamento do arquivo que estamos recebendo.

O código do método TratamentoArquivoRecebido() é o seguinte:

        public void TratamentoArquivoRecebido(int porta, string enderecoIp)
        {
            try
            {
                IPAddress ip = IPAddress.Parse(enderecoIp);
                TcpListener tcpListener = new TcpListener(ip, porta);
                tcpListener.Start();

                while (true)
                {
                    Socket manipularSocket = tcpListener.AcceptSocket();
                    if (manipularSocket.Connected)
                    {
                        string nomeArquivo = string.Empty;
                        NetworkStream networkStream = new NetworkStream(manipularSocket);
                        int thisRead = 0;
                        int blockSize = 1024;
                        Byte[] dataByte = new Byte[blockSize];
                        lock (this)
                        {
                            string caminhoPastaDestino = @"c:\dados";
                            manipularSocket.Receive(dataByte);
                            int tamanhoNomeArquivo = BitConverter.ToInt32(dataByte, 0);
                            nomeArquivo = Encoding.ASCII.GetString(dataByte, 4, tamanhoNomeArquivo);
                            //
                            Stream fileStream = File.OpenWrite(caminhoPastaDestino + nomeArquivo);
                            fileStream.Write(dataByte, 4 + tamanhoNomeArquivo, (1024 - (4 + tamanhoNomeArquivo)));
                            while (true)
                            {
                                thisRead = networkStream.Read(dataByte, 0, blockSize);
                                fileStream.Write(dataByte, 0, thisRead);
                                if (thisRead == 0)
                                    break;
                            }
                            //
                            fileStream.Close();
                        }
                        NovoArquivoRecebido?.Invoke(this, nomeArquivo);
                        manipularSocket = null;
                    }
                }
            }
            catch
            {
                throw;
            }
        }

No código acima temos o código que vai fazer o tratamento do arquivo que esta sendo recebido na porta e no endereço de ip informados.

Na nossa aplicação do Servidor de compartilhamento de arquivos, é fornecido um número de porta, no qual o servidor estará escutando, o endereço ip de origem e um arquivo a ser recebido.

O botão Estabelecer Conexão inicia a escuta na porta indicada quando clicado, invoca o método TratamentoArquivoRecebido em paralelo. Um objeto TcpListener é criado para iniciar a escuta para o arquivo de entrada na porta especificada, quando uma conexão é recebida um arquivo com o mesmo nome que é o arquivo enviado do cliente é reconstruído a partir dos bytes recebidos e salvo na unidade C:\dados e então um Evento é disparado para indicar na interface do usuário do servidor que um novo arquivo foi recebido.

Quando o evento é disparado, todos os assinantes do evento são notificados e, nesse caso, o método de manipulador de eventos Form1_NovoArquivoRecebido é chamado e o código para exibir uma caixa de mensagem e abrir a pasta que contém o arquivo recebido é envolvido em torno do método BeginInvoke para evitar erros na thread.

Simples assim...

Lembrando que esse exemplo é uma das muitas abordagens que podemos fazer para enviar arquivos.

No projeto eu criei uma classe chamada Servidor com o código abaixo onde temos o método Receber que mostra outra forma de receber o arquivo:(Fique a vontade para usar ou não esse método)

using System;
using System.Net.Sockets;
using System.Threading;
namespace TCPServidor
{
    public class Servidor
    {
        public static void Receber(Socket socket, byte[] buffer, int offset, int tamanho, int timeout)
        {
            int startTickCount = Environment.TickCount;
            int recebidos = 0;  // quantos bytes foram recebidos
            do
            {
                if (Environment.TickCount > startTickCount + timeout)
                    throw new Exception("Tempo esgotado...");
                try
                {
                    recebidos += socket.Receive(buffer, offset + recebidos, tamanho - recebidos, SocketFlags.None);
                }
                catch (SocketException ex)
                {
                    if (ex.SocketErrorCode == SocketError.WouldBlock ||
                        ex.SocketErrorCode == SocketError.IOPending ||
                        ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
                    {
                        // o buffer do socket esta vazio , aguarde e tente novamente
                        Thread.Sleep(30);
                    }
                    else
                        throw ex;  //ocorreu um erro
                }
            } while (recebidos < tamanho);
        }
    }
}

A seguir temos um trecho de código de como usar o método Receber :

          Socket socket = tcpClient.Client;
          byte[] buffer = new byte[10]; 
 // tamanho do texto "Alo mundo!"
          try
          {
// recebe dados com timeout igual a 10s
                 Servidor.Receive(socket, buffer, 0, buffer.Length, 10000);
                 string arquivo = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
          }
          catch (Exception ex) { /* ... */ }
     

E assim temos a nossa aplicação TCPServidor pronta para receber arquivos.

Pegue o projeto completo aqui: TCPServidor.zip

(Disse Jesus) Na verdade, na verdade vos digo que aquele que crê em mim tem a vida eterna. João 6:47

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti