2008-09-15 10 views
9

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 se hayan copiado en un búfer y luego regresen. Ese buffer se transmite en segundo plano.NetworkStream.Write devuelve inmediatamente: ¿cómo puedo saber cuándo ha terminado de enviar datos?

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 realmente 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, supongo 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 de envío podrían variar: podría tardar 1 segundo en enviarlo, podría tardar 10 segundos en enviarlo, podría tardar un minuto en 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 "enviando datos al servidor" y "esperando respuesta del servidor". ¿Cómo podría hacerlo?

+0

¿Has probado NetworkStream.Write con un paquete de 100MB? – Calmarius

Respuesta

0

¿Qué le parece usar el método Flush()?

ns.Flush() 

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

+1

El método Flush implementa el método Stream.Flush; sin embargo, como NetworkStream no está almacenado en búfer, no tiene efecto en las transmisiones de red. Llamar al método Flush no arroja una excepción. http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.flush.aspx – GEOCHET

2

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.

1

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.

+0

De hecho, un CMD/ACK es la única forma de lograr lo que quiere. – GEOCHET

-1

Tal vez intente configurar tcp.NoDelay = true

+0

Esto le dice que no use Nagal. Nagal es donde espera ~ 200ms antes de enviar cualquier dato.La idea es que esperar un poco garantiza que va a enviar la mayor cantidad de datos posible, reduciendo el uso de la red. Aún no sabes cuándo se han enviado los datos. –

5

TCP es un protocolo "fiable", 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 usted describe se maneja a través del diseño normal de cliente/servidor, que en su forma más simple es la siguiente ...

El cliente envía una solicitud al servidor y hace un bloqueo de lectura en el zócalo de espera para 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, normalmente ya no le importa lo que sucede, incluso si el socket desaparece durante la transacción, porque le corresponde al cliente iniciar cualquier interacción adicional. Personalmente, me resulta muy reconfortante ser el servidor. :-)

1

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.

1

Intento entender 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).

Por lo tanto, en el diseño de su aplicación, debe seguir este patrón, salvo 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 conjunto, 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.

11

No soy un programador de C#, pero la forma en que hizo 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 la tarjeta de red, exactamente.La mejor manera de pensar en la relación de su programa a la red es:

su programa -> mucha confusión Dalla -> el programa de pares

una lista de cosas que podrían estar en los "lotes de confundir las cosas ":

  • el CLR
  • el sistema operativo interfaz del núcleo
  • una red virtualizada
  • un interruptor
  • un software de cortafuegos cortafuegos
  • un hardware
  • un router de traducción de direcciones de red que realiza
  • un router en la red realización final de traducción de direcciones de los pares

lo tanto, si usted 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, no se han "enviado" hasta que el otro extremo haya enviado un acuse de recibo TCP.

Dicho esto, the documentation for NetworkStream.Write parece indicar que no volverá hasta que al menos ha iniciado la operación de 'enviar':

Los bloques método de escritura hasta que se envíe el número solicitado de bytes o una SocketException es arrojado.

Por supuesto, "se envía" es algo vago por las razones que expuse anteriormente. 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 un Write seguido de un Read de un mensaje que solo emitirá su compañero cuando realmente haya procesado el mensaje.

+0

El paquete de datos TCP se envió siempre tiene un ACK TCP subyacente de su par, y es por eso que usamos TCP vs UDP: siempre sabemos el estado de la conexión. Volviendo a la pregunta, si 'Write (bytes)' llamó, más tarde si el TCP ACK se recibió de otro par, entonces debería aparecer una notificación, esta es la pregunta que el dueño desea. – Shawn

+0

TCP ACK simplemente significa que el sistema operativo * remoto * (no la aplicación remota) ha recibido algunos de sus datos. O posiblemente algún middle-box de red que está haciendo buffering TCP para ayudar a su sistema remoto. No puede confiar en ello para una entrega sólida de los datos de la aplicación. – Glyph

0

Bellow .net son enchufes de Windows que utilizan 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.

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

Cuestiones relacionadas