2010-12-08 14 views
7

Necesito leer desde NetworkStream que enviaría datos aleatoriamente y el tamaño de los paquetes de datos también varía. Estoy implementando una aplicación de subprocesos múltiples en la que cada subproceso tendría su propia secuencia desde la que leer. Si no hay datos en la transmisión, la aplicación debe seguir esperando que lleguen los datos. Sin embargo, si el servidor finaliza el envío de datos y finaliza la sesión, debe salir.Diferencia entre NetworkStream.Read() y NetworkStream.BeginRead()?

Inicialmente utilicé el método Read para obtener los datos de la transmisión, pero solía bloquear el hilo y seguí esperando hasta que aparecieran datos en la transmisión.

La documentación en MSDN sugiere,

Si no hay datos disponibles para su lectura, el método Read devuelve 0. Si el host remoto se cierra la conexión, y todos los datos disponibles ha sido recibida , el método Read completa inmediatamente y devuelve cero bytes.

Pero en mi caso, nunca he obtenido el método Read para devolver 0 y salir con gracia. Solo espera indefinidamente.

En mi investigación posterior, me encontré con BeginRead que observa la transmisión e invoca un método de devolución de llamada de forma asíncrona, tan pronto como recibe los datos. He intentado buscar varias implementaciones con este enfoque, sin embargo, no pude identificar cuándo sería beneficioso usar BeginRead en comparación con Read.

Cuando lo miro, BeginRead tiene la ventaja de tener la llamada asincrónica, que no bloquearía el hilo actual. Pero en mi aplicación, ya tengo un hilo separado para leer y procesar los datos de la transmisión, así que eso no tendría mucha importancia para mí.

  • Puede alguien por favor me ayude a comprender el mecanismo de espera y salida para BeginRead y cómo se diferencia de Read?

  • ¿Cuál sería la mejor manera de implementar la funcionalidad deseada?

+0

¿Está seguro de que el lado remoto está cerrando correctamente la conexión? Nunca tuve un problema con 'Read' que no volviera. Su enfoque de lo contrario parece ser la dirección correcta. –

+0

@Matthew Bueno, sinceramente, no puedo decir eso con certeza. Es un servicio de terceros del que estamos leyendo. Han especificado los tiempos de parada y asumimos que sucede como se mencionó. Solo quiero verificar y volver a verificar en mi extremo antes de levantarles una bandera. –

Respuesta

10

utilizo BeginRead, pero continúan bloqueando el hilo usando una WaitHandle:

byte[] readBuffer = new byte[32]; 
var asyncReader = stream.BeginRead(readBuffer, 0, readBuffer.Length, 
    null, null); 

WaitHandle handle = asyncReader.AsyncWaitHandle; 

// Give the reader 2seconds to respond with a value 
bool completed = handle.WaitOne(2000, false); 
if (completed) 
{ 
    int bytesRead = stream.EndRead(asyncReader); 

    StringBuilder message = new StringBuilder(); 
    message.Append(Encoding.ASCII.GetString(readBuffer, 0, bytesRead)); 
} 

Básicamente se permite un tiempo de espera de la asíncrono lee mediante el WaitHandle y le da un valor booleano (completed) si el la lectura se completó en el tiempo establecido (2000 en este caso).

Aquí está mi código de lectura completo flujo de copiado y pegado de uno de mis proyectos de Windows Mobile:

private static bool GetResponse(NetworkStream stream, out string response) 
{ 
    byte[] readBuffer = new byte[32]; 
    var asyncReader = stream.BeginRead(readBuffer, 0, readBuffer.Length, null, null); 
    WaitHandle handle = asyncReader.AsyncWaitHandle; 

    // Give the reader 2seconds to respond with a value 
    bool completed = handle.WaitOne(2000, false); 
    if (completed) 
    { 
     int bytesRead = stream.EndRead(asyncReader); 

     StringBuilder message = new StringBuilder(); 
     message.Append(Encoding.ASCII.GetString(readBuffer, 0, bytesRead)); 

     if (bytesRead == readBuffer.Length) 
     { 
      // There's possibly more than 32 bytes to read, so get the next 
      // section of the response 
      string continuedResponse; 
      if (GetResponse(stream, out continuedResponse)) 
      { 
       message.Append(continuedResponse); 
      } 
     } 

     response = message.ToString(); 
     return true; 
    } 
    else 
    { 
     int bytesRead = stream.EndRead(asyncReader); 
     if (bytesRead == 0) 
     { 
      // 0 bytes were returned, so the read has finished 
      response = string.Empty; 
      return true; 
     } 
     else 
     { 
      throw new TimeoutException(
       "The device failed to read in an appropriate amount of time."); 
     } 
    } 
} 
+0

Gracias @GenericTypeTea, Este parece ser un enfoque interesante, pero no hay forma de que determine un período de tiempo de espera correcto. Sin embargo, creo que esto se puede modificar un poco para obtener una solución a mi problema. Intentaría e informaría, si tiene éxito. Por cierto, su mango me hizo sonreír .. :) –

+1

Si no me equivoco, siempre debe llamar a EndABC en cada llamada a BeginABC. En el código anterior, si hay un tiempo de espera, nunca se llama a End. Una devolución de llamada que llama a End (y maneja cualquier posible excepción) probablemente debería agregarse al código anterior. – SpeksETC

+0

@SpeksETC - Creo que estás equivocado. Siempre llamo a un final: int bytesRead = stream.EndRead (asyncReader) ;, pero tal vez debería cambiar el código para llamar a ese código directamente después de la línea bool completed = handle.WaitOne (2000, false); para eliminar la duplicación. – GenericTypeTea

4

asíncrono de E/S se pueden utilizar para lograr la misma cantidad de E/S en menos hilos.

Como observa, ahora su aplicación tiene un hilo por Stream.Esto está bien con un pequeño número de conexiones, pero ¿qué sucede si necesita admitir 10000 a la vez? Con la E/S asíncrona, esto ya no es necesario porque la devolución de llamada de finalización de lectura permite pasar el contexto identificando la secuencia relevante. Tus lecturas ya no se bloquean, por lo que no necesitas un hilo por transmisión.

Tanto si utiliza sincronización o asincronización de E/S, existe una forma de detectar y gestionar el cierre de secuencia en los códigos de retorno API relevantes. BeginRead debe fallar con IOException si el socket ya se ha cerrado. Un cierre mientras su lectura asíncrona está pendiente activará una devolución de llamada, y EndRead le dirá el estado del juego.

Cuando la aplicación llama BeginRead, el sistema esperará hasta que los datos es recibido o se produce un error, y luego el sistema utilizará un hilo separado para ejecutar el método de devolución de llamada especificado y bloques en EndRead hasta el NetworkStream provisto lee los datos o arroja una excepción.

+0

Gracias @Steve. El único hilo por flujo se debe a muchas otras consideraciones de diseño y no es realmente un problema. Tendríamos 10 hilos simultáneos al máximo. –

0

BeginRead es un proceso asíncrono que significa que su hilo principal comenzará a ejecutar Leer en otro proceso. Entonces ahora tenemos 2 procesos paralelos. si desea obtener el resultado, debe llamar a EndRead, que dará el resultado.

algunos psudo

BeginRead() 
//...do something in main object while result is fetching in another thread 
var result = EndRead(); 

pero si su hilo principal no tiene nada más que hacer y u tienen que necesitará el resultado, u debe llamar a Lee.

+2

La implicación de que múltiples procesos están involucrados aquí es totalmente incorrecta. –

+0

@ Steve, creo que @ Bonshington significaba 'Thread', cuando dijo 'Process'. –

0

¿Has probado server.ReceiveTimeout? Puede establecer la hora en la que Read() functon esperará los datos entrantes antes de devolver cero. En tu caso, esta propiedad probablemente esté configurada para infinito en alguna parte.