write socket servidor multihilo async c# network-programming

c# - socket - NetworkStream.Write devuelve inmediatamente: ¿cómo puedo saber cuándo ha terminado de enviar datos?



socket tcp c# (9)

¿Qué hay de usar el método Flush ()?

ns.Flush()

Eso debería garantizar que los datos estén escritos antes de continuar.

A pesar de la documentación, NetworkStream.Write no parece esperar hasta que se hayan enviado los datos. En cambio, espera hasta que los datos hayan sido copiados en un buffer y luego regresen. Ese buffer se transmite en segundo plano.

Este es el código que tengo en este momento. Si uso ns.Write o ns.BeginWrite no importa, ambos regresan inmediatamente. EndWrite también regresa inmediatamente (lo cual tiene sentido ya que está escribiendo en el búfer de envío, no escribiendo en la red).

bool done; void SendData(TcpClient tcp, byte[] data) { NetworkStream ns = tcp.GetStream(); done = false; ns.BeginWrite(bytWriteBuffer, 0, data.Length, myWriteCallBack, ns); while (done == false) Thread.Sleep(10); }   public void myWriteCallBack(IAsyncResult ar) { NetworkStream ns = (NetworkStream)ar.AsyncState; ns.EndWrite(ar); done = true; }

¿Cómo puedo saber cuándo se han enviado los datos al cliente?

Quiero esperar 10 segundos (por ejemplo) para recibir una respuesta del servidor después de enviar mis datos; de lo contrario, asumiré que algo estuvo mal. Si tarda 15 segundos en enviar mis datos, siempre se agotarán los tiempos de espera, ya que solo puedo comenzar a contar desde que NetworkStream.Write regrese, es decir, antes de que se hayan enviado los datos. Quiero comenzar a contar 10 segundos desde que los datos han salido de mi tarjeta de red.

La cantidad de datos y el tiempo para enviarlo podrían variar; podría tardar 1 segundo en enviarlo, podría tardar 10 segundos en enviarlo, podría tardar un minuto enviarlo. El servidor envía una respuesta cuando ha recibido los datos (es un servidor smtp), pero no quiero esperar para siempre si mis datos fueron mal formados y la respuesta nunca llegará, por lo que necesito saber si estoy '' Estoy esperando que se envíen los datos, o si estoy esperando que el servidor responda.

Es posible que desee mostrar el estado al usuario. Me gustaría mostrar "enviar datos al servidor" y "esperar respuesta del servidor". ¿Cómo podría hacerlo?


Bellow .net es un socket de Windows que usa TCP. TCP usa paquetes ACK para notificar al remitente que los datos se han transferido con éxito. Entonces, la máquina emisora ​​sabe cuándo se han transferido los datos, pero no hay forma (que yo sepa) de obtener esa información en .net.

editar: Solo una idea, nunca probé: Write () bloquea solo si el buffer de sockets está lleno. Entonces, si bajamos ese tamaño de los buffers (SendBufferSize) a un valor muy bajo (8? 1? 0?) Podemos obtener lo que queremos :)


En general, recomendaría enviar un reconocimiento del cliente de todos modos. De esta forma, puede estar 100% seguro de que los datos se recibieron y se recibieron correctamente.


Intento comprender la intención de los diseñadores de .NET NetworkStream, y deben diseñarlo de esta manera. Después de Escribir, los datos a enviar ya no son manejados por .NET. Por lo tanto, es razonable que Write devuelva inmediatamente (y los datos se enviarán desde NIC pronto).

Entonces, en el diseño de su aplicación, debe seguir este patrón, además de tratar de hacerlo funcionar a su manera. Por ejemplo, usar un tiempo de espera más largo antes de recibir cualquier información de NetworkStream puede compensar el tiempo consumido antes de que su comando salga de la NIC.

En general, es una mala práctica codificar un valor de tiempo de espera dentro de los archivos fuente. Si el valor del tiempo de espera se puede configurar en el tiempo de ejecución, todo debería funcionar bien.


No puedo pensar en un escenario donde NetworkStream.Write no envíe los datos al servidor lo antes posible. Exceptuando la congestión o desconexión masiva de la red, debería terminar en el otro extremo dentro de un tiempo razonable. ¿Es posible que tengas un problema de protocolo? Por ejemplo, con HTTP, los encabezados de las solicitudes deben finalizar con una línea en blanco, y el servidor no enviará ninguna respuesta hasta que ocurra uno: ¿el protocolo en uso tiene una característica similar al final del mensaje?

Aquí hay un código más limpio que su versión original, eliminando el delegado, el campo y Thread.Sleep. Preforma exactamente de la misma manera funcionalmente.

void SendData(TcpClient tcp, byte[] data) { NetworkStream ns = tcp.GetStream(); // BUG?: should bytWriteBuffer == data? IAsyncResult r = ns.BeginWrite(bytWriteBuffer, 0, data.Length, null, null); r.AsyncWaitHandle.WaitOne(); ns.EndWrite(r); }

Parece que la pregunta se modificó mientras escribía lo anterior. El .WaitOne () puede ayudar a su problema de tiempo de espera. Se puede pasar un parámetro de tiempo de espera. Esta es una espera perezosa: el hilo no se volverá a programar hasta que el resultado finalice o el tiempo de espera expire.


No soy un programador de C #, pero la forma en que hiciste esta pregunta es un poco engañosa. La única manera de saber cuándo se han "recibido" sus datos, para cualquier definición útil de "recibido", es tener un mensaje de acuse de recibo específico en su protocolo que indique que los datos se han procesado por completo.

Los datos no "salen" de su tarjeta de red, exactamente. La mejor manera de pensar en la relación de su programa con la red es:

su programa -> muchas cosas confusas -> el programa de pares

Una lista de cosas que podrían estar en "muchas cosas confusas":

  • el CLR
  • el kernel del sistema operativo
  • una interfaz de red virtualizada
  • Un interruptor
  • un firewall de software
  • un firewall de hardware
  • un enrutador que realiza la traducción de direcciones de red
  • un enrutador en el extremo del compañero que realiza la traducción de la dirección de red

Entonces, si está en una máquina virtual, que está alojada en un sistema operativo diferente, que tiene un firewall de software que controla el comportamiento de la red de la máquina virtual, ¿cuándo los datos "realmente" salieron de su tarjeta de red? Incluso en el mejor de los casos, muchos de estos componentes pueden arrojar un paquete, que su tarjeta de red deberá volver a transmitir. ¿Ha "dejado" su tarjeta de red cuando se realizó el primer intento (sin éxito)? La mayoría de las API de red dirían que no, que no han sido "enviadas" hasta que el otro extremo haya enviado un acuse de recibo TCP.

Dicho esto, la documentación de NetworkStream.Write parece indicar que no volverá hasta que al menos haya iniciado la operación de ''envío'':

El método Write bloquea hasta que se envía el número solicitado de bytes o se lanza una SocketException.

Por supuesto, "se envía" es algo vago por las razones que di más arriba. También existe la posibilidad de que los datos sean "realmente" enviados por su programa y recibidos por el programa de pares, pero el par se bloqueará o de lo contrario no procesará los datos. Por lo tanto, debe hacer una Write seguida de una Read de un mensaje que solo emitirá su compañero cuando realmente haya procesado el mensaje.


Si tuviera que adivinar, el NetworkStream considera que los datos se han enviado una vez que transfiere el búfer a Windows Socket. Por lo tanto, no estoy seguro de que haya una forma de lograr lo que desea a través de TcpClient.


TCP es un protocolo "confiable", lo que significa que los datos se recibirán en el otro extremo si no hay errores de socket. He visto numerosos esfuerzos en segundo lugar adivinar TCP con una confirmación de la aplicación de nivel superior, pero en mi humilde opinión, esto es generalmente una pérdida de tiempo y ancho de banda.

Normalmente, el problema que describes se maneja a través del diseño normal de cliente / servidor, que en su forma más simple es así ...

El cliente envía una solicitud al servidor y realiza una lectura de bloqueo en el socket esperando algún tipo de respuesta. Si hay un problema con la conexión TCP, esa lectura abortará. El cliente también debe usar un tiempo de espera para detectar cualquier problema no relacionado con la red con el servidor. Si la solicitud falla o se agota el tiempo, el cliente puede volver a intentar, informar un error, etc.

Una vez que el servidor ha procesado la solicitud y enviado la respuesta, generalmente ya no le importa lo que sucede, incluso si el socket desaparece durante la transacción, porque depende del cliente iniciar cualquier interacción adicional. Personalmente, me resulta muy reconfortante ser el servidor. :-)


Tal vez intente configurar tcp.NoDelay = true