socket servidor net example ejemplo comunicacion cliente c# network-programming tcpclient

servidor - tcp server c#



Enviar y recibir datos a través de una red usando TcpClient (4)

En primer lugar, TCP no garantiza que todo lo que envíe se recibirá con la misma lectura en el otro extremo. Solo garantiza que todos los bytes que envíe llegarán y en el orden correcto.

Por lo tanto, deberá seguir creando un búfer al leer de la transmisión. También deberá saber qué tan grande es cada mensaje.

Lo más simple es utilizar un carácter ASCII no tipable para marcar el final del paquete y buscarlo en los datos recibidos.

Necesito desarrollar un servicio que se conecte a un servidor TCP. Las tareas principales son leer los mensajes entrantes y también enviar comandos al servidor en diez minutos, como un comando de sincronización. Por ejemplo, utilicé el objeto TcpClient como se muestra a continuación:

... TcpClient tcpClient = new TcpClient(); tcpClient.Connect("x.x.x.x", 9999); networkStream = tcpClient.GetStream(); clientStreamReader = new StreamReader(networkStream); clientStreamWriter = new StreamWriter(networkStream); while(true) { clientStreamReader.Read() }

Además, cuando necesito escribir algo en cualquier método, uso:

clientStreamWriter.write("xxx");

¿Es este uso correcto? ¿O hay un mejor camino?


He tenido suerte al usar el objeto socket directamente (en lugar del cliente TCP). Creo un objeto Servidor que se parece a esto (he editado algunas cosas, como el manejo de excepciones para abreviar, pero espero que la idea se manifieste) ...

public class Server() { private Socket sock; // You''ll probably want to initialize the port and address in the // constructor, or via accessors, but to start your server listening // on port 8080 and on any IP address available on the machine... private int port = 8080; private IPAddress addr = IPAddress.Any; // This is the method that starts the server listening. public void Start() { // Create the new socket on which we''ll be listening. this.sock = new Socket( addr.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // Bind the socket to the address and port. sock.Bind(new IPEndPoint(this.addr, this.port)); // Start listening. this.sock.Listen(this.backlog); // Set up the callback to be notified when somebody requests // a new connection. this.sock.BeginAccept(this.OnConnectRequest, sock); } // This is the method that is called when the socket recives a request // for a new connection. private void OnConnectRequest(IAsyncResult result) { // Get the socket (which should be this listener''s socket) from // the argument. Socket sock = (Socket)result.AsyncState; // Create a new client connection, using the primary socket to // spawn a new socket. Connection newConn = new Connection(sock.EndAccept(result)); // Tell the listener socket to start listening again. sock.BeginAccept(this.OnConnectRequest, sock); } }

Luego, utilizo una clase de conexión separada para administrar la conexión individual con el host remoto. Eso se parece a esto ...

public class Connection() { private Socket sock; // Pick whatever encoding works best for you. Just make sure the remote // host is using the same encoding. private Encoding encoding = Encoding.UTF8; public Connection(Socket s) { this.sock = s; // Start listening for incoming data. (If you want a multi- // threaded service, you can start this method up in a separate // thread.) this.BeginReceive(); } // Call this method to set this connection''s socket up to receive data. private void BeginReceive() { this.sock.BeginReceive( this.dataRcvBuf, 0, this.dataRcvBuf.Length, SocketFlags.None, new AsyncCallback(this.OnBytesReceived), this); } // This is the method that is called whenever the socket receives // incoming bytes. protected void OnBytesReceived(IAsyncResult result) { // End the data receiving that the socket has done and get // the number of bytes read. int nBytesRec = this.sock.EndReceive(result); // If no bytes were received, the connection is closed (at // least as far as we''re concerned). if (nBytesRec <= 0) { this.sock.Close(); return; } // Convert the data we have to a string. string strReceived = this.encoding.GetString( this.dataRcvBuf, 0, nBytesRec); // ...Now, do whatever works best with the string data. // You could, for example, look at each character in the string // one-at-a-time and check for characters like the "end of text" // character (''/u0003'') from a client indicating that they''ve finished // sending the current message. It''s totally up to you how you want // the protocol to work. // Whenever you decide the connection should be closed, call // sock.Close() and don''t call sock.BeginReceive() again. But as long // as you want to keep processing incoming data... // Set up again to get the next chunk of data. this.sock.BeginReceive( this.dataRcvBuf, 0, this.dataRcvBuf.Length, SocketFlags.None, new AsyncCallback(this.OnBytesReceived), this); } }

Puede usar su objeto Connection para enviar datos llamando directamente a su Socket, así ...

this.sock.Send(this.encoding.GetBytes("Hello to you, remote host."));

Como dije, intenté editar el código aquí para publicarlo, así que me disculpo si hay algún error en él.


Por cierto, puede usar la tecnología de serialización para enviar cadenas, números o cualquier objeto compatible con la serialización (la mayoría de las clases y estructuras de almacenamiento de datos .NET son [Serializables]). Allí, primero debe enviar Int32-length en cuatro bytes a la transmisión y luego enviar datos binarios serializados (System.Runtime.Serialization.Formatters.Binary.BinaryFormatter).

En el otro lado o la conexión (en realidad, en ambos lados) definitivamente deberías tener un búfer de bytes [] que agregarás y recortarás en el tiempo de ejecución cuando lleguen los datos.

Algo así como lo estoy usando:

namespace System.Net.Sockets { public class TcpConnection : IDisposable { public event EvHandler<TcpConnection, DataArrivedEventArgs> DataArrive = delegate { }; public event EvHandler<TcpConnection> Drop = delegate { }; private const int IntSize = 4; private const int BufferSize = 8 * 1024; private static readonly SynchronizationContext _syncContext = SynchronizationContext.Current; private readonly TcpClient _tcpClient; private readonly object _droppedRoot = new object(); private bool _dropped; private byte[] _incomingData = new byte[0]; private Nullable<int> _objectDataLength; public TcpClient TcpClient { get { return _tcpClient; } } public bool Dropped { get { return _dropped; } } private void DropConnection() { lock (_droppedRoot) { if (Dropped) return; _dropped = true; } _tcpClient.Close(); _syncContext.Post(delegate { Drop(this); }, null); } public void SendData(PCmds pCmd) { SendDataInternal(new object[] { pCmd }); } public void SendData(PCmds pCmd, object[] datas) { datas.ThrowIfNull(); SendDataInternal(new object[] { pCmd }.Append(datas)); } private void SendDataInternal(object data) { if (Dropped) return; byte[] bytedata; using (MemoryStream ms = new MemoryStream()) { BinaryFormatter bf = new BinaryFormatter(); try { bf.Serialize(ms, data); } catch { return; } bytedata = ms.ToArray(); } try { lock (_tcpClient) { TcpClient.Client.BeginSend(BitConverter.GetBytes(bytedata.Length), 0, IntSize, SocketFlags.None, EndSend, null); TcpClient.Client.BeginSend(bytedata, 0, bytedata.Length, SocketFlags.None, EndSend, null); } } catch { DropConnection(); } } private void EndSend(IAsyncResult ar) { try { TcpClient.Client.EndSend(ar); } catch { } } public TcpConnection(TcpClient tcpClient) { _tcpClient = tcpClient; StartReceive(); } private void StartReceive() { byte[] buffer = new byte[BufferSize]; try { _tcpClient.Client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, DataReceived, buffer); } catch { DropConnection(); } } private void DataReceived(IAsyncResult ar) { if (Dropped) return; int dataRead; try { dataRead = TcpClient.Client.EndReceive(ar); } catch { DropConnection(); return; } if (dataRead == 0) { DropConnection(); return; } byte[] byteData = ar.AsyncState as byte[]; _incomingData = _incomingData.Append(byteData.Take(dataRead).ToArray()); bool exitWhile = false; while (exitWhile) { exitWhile = true; if (_objectDataLength.HasValue) { if (_incomingData.Length >= _objectDataLength.Value) { object data; BinaryFormatter bf = new BinaryFormatter(); using (MemoryStream ms = new MemoryStream(_incomingData, 0, _objectDataLength.Value)) try { data = bf.Deserialize(ms); } catch { SendData(PCmds.Disconnect); DropConnection(); return; } _syncContext.Post(delegate(object T) { try { DataArrive(this, new DataArrivedEventArgs(T)); } catch { DropConnection(); } }, data); _incomingData = _incomingData.TrimLeft(_objectDataLength.Value); _objectDataLength = null; exitWhile = false; } } else if (_incomingData.Length >= IntSize) { _objectDataLength = BitConverter.ToInt32(_incomingData.TakeLeft(IntSize), 0); _incomingData = _incomingData.TrimLeft(IntSize); exitWhile = false; } } StartReceive(); } public void Dispose() { DropConnection(); } } }

Eso es solo un ejemplo, debe editarlo para su uso.


Primero, recomiendo que use WCF, .NET Remoting u otra abstracción de comunicación de alto nivel. La curva de aprendizaje para enchufes "simples" es casi tan alta como WCF, porque hay muchas trampas no obvias cuando se usa TCP / IP directamente.

Si decide continuar por la ruta TCP / IP, revise mis preguntas frecuentes sobre TCP / IP de .NET , particularmente las secciones sobre el marco de mensaje y las especificaciones del protocolo de la aplicación .

Además, use API de socket asíncrono. Las API síncronas no se escalan y en algunas situaciones de error pueden provocar interbloqueos. Las API síncronas constituyen un código de ejemplo bastante pequeño, pero el código de calidad de producción del mundo real usa las API asíncronas.