NetworkStream.Read会挂起/阻止的原因是什么?

时间:2011-08-05 14:52:01

标签: c# sockets blocking networkstream

MSDN文档似乎暗示NetworkStream.Read将始终立即返回。如果没有找到数据,则返回0.但是,我有一些当前部署的代码,仅在某些情况下(我还没有想出哪些代码),NetworkStream.Read似乎挂起。这是我能够从转储文件中收集的堆栈跟踪

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)

我注意到NetworkStrea.Read实际上调用了Socket.Receive,据我所知,它可以阻塞。我只是不知道为什么有时它会阻止,有时它不会。

4 个答案:

答案 0 :(得分:26)

NetworkStream.Read文档的备注部分具有误导性。它说:

  

此方法将数据读入buffer参数并返回成功读取的字节数。 如果没有可用于读取的数据,则Read方法返回0. Read操作读取尽可能多的数据,最多为size参数指定的字节数。如果远程主机关闭连接,并且已收到所有可用数据,则Read方法立即完成并返回零字节。

应该说:

  

此方法将数据读入buffer参数并返回成功读取的字节数。 如果没有可用于读取的数据,则Read方法将阻塞,直到数据可用或连接已关闭。读取操作将读取尽可能多的数据,最多为大小指定的字节数参数。如果远程主机关闭连接,并且已收到所有可用数据,则Read方法立即完成并返回零字节。

答案 1 :(得分:7)

有时套接字缓冲区中已经存在数据,有时会出现预先设定的数据。

看到NetworkStream块的一个常见原因是连接的每一侧都希望另一侧关闭。例如,如果您建立HTTP 1.1保持连接,但仍然执行“读取直到连接关闭”获取内容的方式。

答案 2 :(得分:3)

处理NetworkStream时遇到的一个常见错误是通过Write方法发送未完成的命令,导致连续的Read呼叫挂起。

请参阅以下示例,该示例尝试将用户名发送到打开的FTP端口。它希望像 331这样的响应请指定密码,但Read方法挂起:

var request = Encoding.ASCII.GetBytes("user [username]");
networkStream.Write(request, 0, request.Length);
var streamReader = new StreamReader(networkStream);
var response = streamReader.ReadLine(); // <-- hangs

一个神奇的解决方案是用以下内容替换第一行:

var request = Encoding.ASCII.GetBytes("user [username] \r\n");

只需在命令末尾添加 \ r \ n 短语,一切都将按预期开始工作。

答案 3 :(得分:0)

如果没有可用于读取的数据,则Read方法将阻塞,直到数据可用。如果您不想阻止,请考虑使用Async Socket功能。 http://msdn.microsoft.com/en-us/library/bbx2eya8.aspx