Essa semana precisei fazer um server e um client usando TCP. Depois de muita busca achei a melhor solução no livro C# CookBook 2nd Edition da Editora O´Reilly. Mas claro que no livro a solução não estava 100%, faltava declarar uns namespaces e um objeto.
No livro o autor cita que será usado o System.Net mas assim que comecei a ler o codigo percebi que ia precisar de outras namespaces, então declarei as seguintes:
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
No meu caso utilizei as listadas a cima mas para seguir o exemplo do livro também é necessário declarar a seguinte namespace para utilizar o Trace:
using System.Diagnostics;
Com isso fiz a seguinte alteração no codigo, troquei o Trace.WriteLine por Console.WriteLine, assim consigo ver todos os resultados no console, quando o Trace aparece apenas no output do debug.
Como a classe é meio grande vou fatiar em pedaços para ficar mais claro mas fica o link para baixar o codigo.
A estrutura inicial da classe é a seguinte:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
namespace TCP_UDP_Server
{
public class TCPServer
{
private TcpListener listener = null;
private IPAddress address;
private int port;
private bool listening = false;
private object _syncRoot;
public TCPServer(IPAddress address, int port)
{
this.address = address;
this.port = port;
_syncRoot = this;
}
public void Listen()
{
//CODE HERE
}
public void StopListening()
{
if (listening)
{
lock (_syncRoot)
{
listening = false;
listener.Stop();
}
}
}
private void ProcessClient(object client)
{
//CODE HERE
}
public IPAddress Address
{
get { return address; }
}
public int Port
{
get { return port; }
}
public bool Listening
{
get { return listening; }
}
}
}
No método Listen() é criado uma escuta para o endereço e a porta configurada. Após isso fica escutando a porta por mensagens do client(o client não vou comentar nesse post, vou criar outro só para explicar sua funcionalidade).
try
{
lock (_syncRoot)
{
listener = new TcpListener(address, port);
listener.Start();
listening = true;
}
do
{
Console.Write("Looking for someone to talk to... ");
TcpClient newClient = listener.AcceptTcpClient();
Console.Write("Connected to new client");
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessClient), newClient);
} while (listening);
}
catch (SocketException se)
{
Console.WriteLine("SocketException: ", se.ToString());
}
finally
{
StopListening();
}
E no método ProcessClient() é recebido o pacote e transformado em uma string. Ao receber os dados retorna uma mensagem para o client avisando o recebimento.
TcpClient newClient = (TcpClient)client;
try
{
byte[] bytes = new byte[1024];
StringBuilder clientData = new StringBuilder();
using (NetworkStream ns = newClient.GetStream())
{
ns.ReadTimeout = 60000;
int bytesRead = 0;
do
{
try
{
bytesRead = ns.Read(bytes, 0, bytes.Length);
if (bytesRead > 0)
{
clientData.Append(Encoding.ASCII.GetString(bytes, 0, bytesRead));
ns.ReadTimeout = 3000;
}
}
catch (IOException ioe)
{
Console.WriteLine("Read time out: "+ ioe.Message);
bytesRead = 0;
}
} while (bytesRead > 0);
Console.WriteLine("Client says: " + clientData.ToString());
bytes = Encoding.ASCII.GetBytes("Thanks call again!");
ns.Write(bytes, 0, bytes.Length);
}
}
finally
{
if (newClient != null)
newClient.Close();
}