c# - visual - ¿Cuáles son algunas de las razones por las que NetworkStream.Read se bloqueará/bloqueará?
websocket visual studio (4)
La documentación de MSDN parece sugerir que NetworkStream.Read siempre regresará inmediatamente. Si no se encuentran datos, devuelve 0. Sin embargo, tengo un código que se implementa actualmente, que solo en algunos casos (y aún no he descubierto cuáles), NetworkStream.Read parece bloquearse. Aquí está el seguimiento de la pila que pude recopilar de un archivo de volcado
00000000705ae850 000007fef784f60d DomainBoundILStubClass.IL_STUB(IntPtr, Byte*, Int32, System.Net.Sockets.SocketFlags) 00000000705ae930 000007fef785c930 System.Net.Sockets.Socket.Receive(Byte[], Int32, Int32, System.Net.Sockets.SocketFlags, System.Net.Sockets.SocketError ByRef) 00000000705ae9b0 000007ff004eb668 System.Net.Sockets.NetworkStream.Read(Byte[], Int32, Int32) 00000000705aea40 000007fef784e6ae MySocketStuff.SocketConnectCallback(System.IAsyncResult) 00000000705aeb20 000007fef84f2bbb System.Net.LazyAsyncResult.Complete(IntPtr) 00000000705aeb90 000007fef7853c7b System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) 00000000705aebe0 000007fef784e5d3 System.Net.ContextAwareResult.Complete(IntPtr) 00000000705aec40 000007fef7d027f9 System.Net.LazyAsyncResult.ProtectedInvokeCallback(System.Object, IntPtr) 00000000705aeca0 000007fef8b9815e System.Net.Sockets.Socket.ConnectCallback() 00000000705aed20 000007fef93e14c2 System.Threading._ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(System.Object, Boolean)
Observé que NetworkStrea.Read realmente llama a Socket.Receive, que puede estar bloqueando por lo que yo entiendo. Simplemente no sé por qué a veces bloqueaba y otras veces no.
A veces, ya habrá datos en el búfer del zócalo y, a veces, no los habrá, por supuesto.
Una razón común para ver un bloque de NetworkStream
es si cada lado de la conexión espera que el otro se cierre. Por ejemplo, si hace una conexión HTTP 1.1 keep-alive, pero sigue haciendo la forma de "leer hasta que se cierre la conexión" para obtener contenido.
La sección Comentarios de la documentación de NetworkStream.Read es engañosa. Dice:
Este método lee los datos en el parámetro del búfer y devuelve la cantidad de bytes leídos con éxito. Si no hay datos disponibles para la lectura, el método Read devuelve 0. La operación de lectura lee tantos datos como estén disponibles, hasta la cantidad de bytes especificada por el parámetro de tamaño. Si el host remoto apaga la conexión y se han recibido todos los datos disponibles, el método Read se completa inmediatamente y devuelve cero bytes.
Debería decir:
Este método lee los datos en el parámetro del búfer y devuelve la cantidad de bytes leídos con éxito. Si no hay datos disponibles para la lectura, el método Read bloquea hasta que los datos estén disponibles o la conexión se cierre. La operación de lectura lee tantos datos como están disponibles, hasta la cantidad de bytes especificada por el parámetro de tamaño. Si el host remoto apaga la conexión y se han recibido todos los datos disponibles, el método Read se completa inmediatamente y devuelve cero bytes.
Si no hay datos disponibles para la lectura, el método Read se bloqueará hasta que los datos estén disponibles. Considere usar las funciones Async Socket si no quiere bloquear. http://msdn.microsoft.com/en-us/library/bbx2eya8.aspx
Un error común al tratar con NetworkStream
es enviar comandos no finalizados a través del método Write
, lo que hace que se cuelgue una llamada Read
consecutiva.
Vea el siguiente ejemplo que intenta enviar un nombre de usuario a un puerto FTP abierto. Espera una respuesta como 331. Especifique la contraseña, pero el método de Read
se bloquea:
var request = Encoding.ASCII.GetBytes("user [username]");
networkStream.Write(request, 0, request.Length);
var streamReader = new StreamReader(networkStream);
var response = streamReader.ReadLine(); // <-- hangs
Una solución mágica es reemplazar la primera línea con lo siguiente:
var request = Encoding.ASCII.GetBytes("user [username] /r/n");
Simplemente agregando / r / n frase al final del comando, todo comenzará a funcionar como se esperaba.