我有一个应用程序使用 TCP 套接字来交换字节数组,这些数组在大多数情况下包含 JSON 字符串数据。我遇到的是,对于较大的消息和不太理想的网络条件,使用 NetworkStream.DataAvailable 似乎不是检测消息结束的可靠方法。似乎在某些情况下 DataAvailable 被设置为 false,即使只有部分消息已被对等方传输(使用 TcpClient.GetStream().Write(data, 0, data.Length
) 。这会导致不完整的数据被传回应用程序,在 JSON 消息的情况下,这意味着反序列化失败。
我尝试了两种表现出相同问题的实现:
实现 1:
byte[] Data;
byte[] buffer = new byte[2048];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = ClientStream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
BytesRead += read;
if (!ClientStream.DataAvailable) break;
}
Data = ms.ToArray();
}
实现 2:
byte[] Data;
using (MemoryStream ms = new MemoryStream())
{
byte[] buffer = new byte[2048];
do
{
int read = ClientStream.Read(buffer, 0, buffer.Length);
if (read > 0)
{
ms.Write(buffer, 0, read);
BytesRead += read;
}
}
while (ClientStream.DataAvailable);
Data = ms.ToArray();
}
似乎一种非常有效但完全次优的解决方案是添加 Thread.Sleep 以防 NetworkStream.DataAvailable 为 false(在循环内时)以允许传递数据。然而,这严重限制了我想避免的整体 IOPS,即
实现 3(可行,但不是最优的)
byte[] Data;
using (MemoryStream ms = new MemoryStream())
{
byte[] buffer = new byte[2048];
do
{
int read = ClientStream.Read(buffer, 0, buffer.Length);
if (read > 0)
{
ms.Write(buffer, 0, read);
BytesRead += read;
}
if (!ClientStream.DataAvailable) System.Threading.Thread.Sleep(250);
}
while (ClientStream.DataAvailable);
Data = ms.ToArray();
}
我真的很想找到一种方法来保持循环,直到所有数据都被传送。正如我提到的,我正在客户端上执行从零到数据长度的简单写入操作,所以我不认为那里有问题。
有没有人有过这样的经历和推荐?
最佳答案
似乎 .DataAvailable 确实可靠,而且由于数据通过网络到达的速度可能比从流中读取数据的速度慢,因此 .DataAvailable 可以在我的应用程序认为的开始和结束之间来回切换一条消息。
我正在回答并关闭此问题,因为我认为唯一的解决方案是:
1) 添加一个over-arching接收超时值,并在读取循环中执行thread.sleep,并在达到接收超时后终止操作
2) 实现一些指示数据负载大小的机制 - 显式地或通过创建元数据 header 系统 - 以指示应读取多少数据,并在读取了这么多数据或操作超时后退出
这两个是我能想到的最好的,并且似乎已被其他基于 TCP 的协议(protocol)(如 HTTP)和通常的任何其他 RPC 之类的协议(protocol)所验证。
希望这可以节省一些时间。
关于C# NetworkStream.DataAvailable 似乎不可靠,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36731247/