TcpClient.GetStream().DataAvailable 返回 false,但流有更多数据
因此,阻塞 Read() 似乎可以在接收完发送给它的所有数据之前返回。反过来,我们用一个循环包装 Read(),该循环由相关流中的 DataAvailable 值控制。问题是您可以在此 while 循环中接收更多数据,但没有进行幕后处理来让系统知道这一点。我在网上找到的大多数解决方案都不适用于我。
我最终所做的是作为循环的最后一步,我在从流中读取每个块后执行一个简单的 Thread.Sleep(1) 。这似乎给了系统时间来更新,但我没有得到准确的结果,但这似乎有点老套,而且对于解决方案来说有点“间接”。
以下是我正在处理的情况的列表: IIS 应用程序和独立应用程序之间的单一 TCP 连接,两者都是用 C# 编写的,用于发送/接收通信。它发送请求,然后等待响应。该请求是由 HTTP 请求发起的,但我在从 HTTP 请求读取数据时没有遇到此问题,这是事后发生的。
以下是处理传入连接的基本代码,
protected void OnClientCommunication(TcpClient oClient)
{
NetworkStream stream = oClient.GetStream();
MemoryStream msIn = new MemoryStream();
byte[] aMessage = new byte[4096];
int iBytesRead = 0;
while ( stream.DataAvailable )
{
int iRead = stream.Read(aMessage, 0, aMessage.Length);
iBytesRead += iRead;
msIn.Write(aMessage, 0, iRead);
Thread.Sleep(1);
}
MemoryStream msOut = new MemoryStream();
// .. Do some processing adding data to the msOut stream
msOut.WriteTo(stream);
stream.Flush();
oClient.Close();
}
欢迎所有反馈以获得更好的解决方案,或者只是对需要尝试一下 Sleep(1) 以允许在检查 DataAvailable 值之前正确更新内容表示赞同。
我想我希望两年后能够得到这个问题的答案 事情已经不是这样了:)
So, it would seem that a blocking Read() can return before it is done receiving all of the data being sent to it. In turn we wrap the Read() with a loop that is controlled by the DataAvailable value from the stream in question. The problem is that you can receive more data while in this while loop, but there is no behind the scenes processing going on to let the system know this. Most of the solutions I have found to this on the net have not been applicable in one way or another to me.
What I have ended up doing is as the last step in my loop, I do a simple Thread.Sleep(1) after reading each block from the stream. This appears to give the system time to update and I am not getting accurate results but this seems a bit hacky and quite a bit 'circumstantial' for a solution.
Here is a list of the circumstances I am dealing with: Single TCP Connection between an IIS Application and a standalone application, both written in C# for send/receive communication. It sends a request and then waits for a response. This request is initiated by an HTTP request, but I am not having this issue reading data from the HTTP Request, it is after the fact.
Here is the basic code for handling an incoming connection
protected void OnClientCommunication(TcpClient oClient)
{
NetworkStream stream = oClient.GetStream();
MemoryStream msIn = new MemoryStream();
byte[] aMessage = new byte[4096];
int iBytesRead = 0;
while ( stream.DataAvailable )
{
int iRead = stream.Read(aMessage, 0, aMessage.Length);
iBytesRead += iRead;
msIn.Write(aMessage, 0, iRead);
Thread.Sleep(1);
}
MemoryStream msOut = new MemoryStream();
// .. Do some processing adding data to the msOut stream
msOut.WriteTo(stream);
stream.Flush();
oClient.Close();
}
All feedback welcome for a better solution or just a thumbs up on needing to give that Sleep(1) a go to allow things to update properly before we check the DataAvailable value.
Guess I am hoping after 2 years that the answer to this question isn't how things still are :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
你必须知道你需要读取多少数据;您不能简单地循环读取数据,直到不再有数据为止,因为您永远无法确定不会再有数据了。
这就是为什么 HTTP GET 结果在 HTTP 标头中有一个字节计数:这样客户端就会知道它何时收到了所有数据。
根据您是否可以控制对方发送的内容,这里有两种解决方案:
使用“帧”字符:(SB)data(EB),其中 SB 和 EB 是起始块和结束块字符(您选择的)但不能出现在数据内。当您“看到”EB 时,您就知道您已完成。
在每条消息前面实现一个长度字段,以指示后面有多少数据:(len)data。读(len),再读(len)个字节;根据需要重复。
这与从文件中读取数据不同,其中零长度读取意味着数据结束(这确实意味着另一端已断开连接,但那是另一个故事)。
第三种(不推荐)解决方案是您可以实现计时器。 一旦开始获取数据,请设置计时器。如果接收循环空闲一段时间(如果数据不经常到来,比如几秒钟),您可能会假设没有更多数据到来。最后一种方法是最后的手段……它不太可靠,难以调整,而且很脆弱。
You have to know how much data you need to read; you cannot simply loop reading data until there is no more data, because you can never be sure that no more is going to come.
This is why HTTP GET results have a byte count in the HTTP headers: so the client side will know when it has received all the data.
Here are two solutions for you depending on whether you have control over what the other side is sending:
Use "framing" characters: (SB)data(EB), where SB and EB are start-block and end-block characters (of your choosing) but which CANNOT occur inside the data. When you "see" EB, you know you are done.
Implement a length field in front of each message to indicate how much data follows: (len)data. Read (len), then read (len) bytes; repeat as necessary.
This isn't like reading from a file where a zero-length read means end-of-data (that DOES mean the other side has disconnected, but that's another story).
A third (not recommended) solution is that you can implement a timer. Once you start getting data, set the timer. If the receive loop is idle for some period of time (say a few seconds, if data doesn't come often), you can probably assume no more data is coming. This last method is a last resort... it's not very reliable, hard to tune, and it's fragile.
我发现这有问题。
您期望通信速度比
while()
循环更快,但这是不太可能的。while() 循环一旦没有更多数据就会结束,但在退出后几毫秒可能不会出现这种情况。
您是否期望一定数量的字节?
OnClientCommunication()
多久被触发一次?谁触发它?在
while()
循环之后如何处理数据?您是否继续附加以前的数据?DataAvailable
WILL 返回 false,因为您的读取速度比通信速度快,因此只有当您不断返回此代码块来处理更多传入数据时才可以。I'm seeing a problem with this.
You're expecting that the communication will be faster than the
while()
loop, which is very unlikely.The
while()
loop will finish as soon as there is no more data, which may not be the case a few milliseconds just after it exits.Are you expecting a certain amount of bytes?
How often is
OnClientCommunication()
fired? Who triggers it?What do you do with the data after the
while()
loop? Do you keep appending to previous data?DataAvailable
WILL return false because you're reading faster than the communication, so that's fine only if you keep coming back to this code block to process more data coming in.我试图在从网络流读取数据之前检查 DataAvailable,它会返回 false,尽管在读取单个字节后它会返回 true。于是我查了MSDN文档,他们在查之前也看了。我会将 while 循环重新安排为 do while 循环以遵循此模式。
http://msdn.microsoft.com/en -us/library/system.net.sockets.networkstream.dataavailable.aspx
I was trying to check DataAvailable before reading data from a network stream and it would return false, although after reading a single byte it would return true. So I checked the MSDN documentation and they also read before checking. I would re-arrange the while loop to a do while loop to follow this pattern.
http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.dataavailable.aspx
当我有这个代码时:
从我可以观察到:
当我有这段代码时:
那么 NetworkStream 有足够的时间来正确设置 .DataAvailable 并且此方法应该正确运行。
有趣的事实......这似乎在某种程度上取决于操作系统版本。因为第一个没有睡眠的函数在 Win XP 和 Win 10 上对我有效,但在 Win 7 上无法接收整个 1000 字节。不要问我为什么,但我对其进行了相当彻底的测试,并且很容易重现。
When I have this code:
From what I can observe:
When I have this code:
Then the NetworkStream have enough time to properly set .DataAvailable and this method should function correctly.
Fun fact... This seems to be somehow OS Version dependent. Because the first function without sleep worked for me on Win XP and Win 10, but was failing to receive whole 1000 bytes on Win 7. Don't ask me why, but I tested it quite thoroughly and it was easily reproducible.
使用 TcpClient.Available 将允许此代码每次准确读取可用的内容。当剩余要读取的数据量大于或等于 TcpClient.ReceiveBufferSize 时,TcpClient.Available 自动设置为 TcpClient.ReceiveBufferSize。否则它被设置为剩余数据的大小。
因此,您可以通过设置 TcpClient.ReceiveBufferSize(例如,oClient.ReceiveBufferSize = 4096;)来指示每次读取可用的最大数据量。
Using TcpClient.Available will allow this code to read exactly what is available each time. TcpClient.Available is automatically set to TcpClient.ReceiveBufferSize when the amount of data remaining to be read is greater than or equal to TcpClient.ReceiveBufferSize. Otherwise it is set to the size of the remaining data.
Hence, you can indicate the maximum amount of data that is available for each read by setting TcpClient.ReceiveBufferSize (e.g., oClient.ReceiveBufferSize = 4096;).
此示例介绍了一种用于发送和接收数据(即文本消息)的算法。您还可以发送文件。
This example presents an algorithm for sending and receiving data, namely text messages. You can also send files.
使用 do-while 循环。这将确保内存流指针已移动。第一个 Read 或 ReadAsync 将导致内存流指针移动,然后“.DataAvailable”属性将继续返回 true,直到到达流末尾。
微软文档的示例:
原始微软文档
Use a do-while loop. This will make sure the memory stream pointers have moved. The first Read or ReadAsync will cause the memorystream pointer to move and then onwards the ".DataAvailable" property will continue to return true until we hit the end of the stream.
An example from microsoft docs:
Original Micorosoft Doc