13

Estoy usando un NetworkStream & TcpClient para recibir datos de manera asincrónica usando BeginRead. Necesito aplicar un tiempo de espera a esta operación, de modo que después de un período de tiempo especificado la lectura se anule.BeginReceive/BeginRead timeouts

Por lo que puedo decir, esto no es compatible con NetworkStream o TcpClient: hay una propiedad ReceiveTimeout, pero esto parece aplicarse solo al equivalente síncrono: 'Leer'.

Incluso la clase Socket subyacente no parece admitir los tiempos de espera en su método BeginReceive.

He buscado en este tema y la única resolución sugerida que he visto es configurar otra cadena de fondo para cancelar la operación si no se completa dentro del período de tiempo de espera. Esto parece un hack horrible. Seguramente hay una mejor manera?

Respuesta

2

Es la única forma de hacerlo, porque cuando está utilizando una operación asíncrona, el hilo que inició la operación se desactiva haciendo otra cosa. El tiempo de espera está disponible con la versión síncrona porque el hilo de ejecución está bloqueado hasta que se complete la operación de lectura.

Sin embargo, si tuviera que usar un hilo de fondo para cancelar la operación, no tendría sentido seguir utilizando los métodos asincrónicos de inicio/finalización. Si va a derivar un hilo de fondo, solo realice una operación de lectura sincrónica desde el hilo de fondo, y luego puede usar el ReceiveTimeout.

+1

Joel nos apunta en la dirección correcta acerca de no usar un subproceso de fondo para cancelar, pero en caso que no es obvio, lo que mucha gente no es tener 1 hilo (tal vez de la encuesta de trabajadores) periódicamente limpia las conexiones muertas. Esto es necesario para liberar manejadores de socket/etc, y especialmente importante si alguna vez recibes un ataque de denegación de servicio intencional o no. – eselk

+0

La otra opción es ['System.Threading.Timer'] (http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx), que no ocupa un hilo. – hangar

0

Espere en ManualResetEvent con algún valor de tiempo de espera para indicar cuándo finaliza su tarea. Si se agota antes de que se señalice, entonces sabrá que la operación asincrónica nunca se completó.

private ManualResetEvent receiveDone = new ManualResetEvent(false); 

receiveDone.Reset(); 
socket.BeginReceive(...); 
if(!receiveDone.WaitOne(new TimeSpan(0, 0, 0, 30))) //wait for 30 sec. 
    throw new SocketException((int)SocketError.TimedOut); 

Dentro BeginReceive de devolución de llamada, utilizar

private void ReceiveCallBack(IAsyncResult ar) 
{ 
    /** Use ar to check if receive is correct and complete */ 
    receiveDone.Set(); 
} 
+1

Downvoting porque esto es exactamente lo que hice (ingenuamente), y no funciona. Cada llamada a 'BeginReceive' que no coincida con una llamada a' EndReceive' perderá recursos del kernel. Graficar la actividad del proceso en algo como Perfmon y ver el grupo no paginado * lentamente * gotear. Si esto dura lo suficiente (en mi caso, horas) eventualmente obtendrá una excepción con el código de error 'WSAENOBUFS'; "No se pudo realizar una operación en un socket porque el sistema carecía de suficiente espacio en el búfer o porque una cola estaba llena". – Anthony

+0

No lo he verificado, pero ¿no podemos solucionar el problema llamando a 'socket.EndReceive (..)' antes de lanzar una excepción? IAsyncResult que necesita para llamar a 'EndReceive' la devuelve 'BeginReceive'. Votando tu comentario. Gracias por la actualización. –

+0

Sí, esto podría funcionar. – Anthony

Cuestiones relacionadas