我有一个使用TCP套接字交换字节数组的应用程序,在大多数情况下,这些字节数组包含JSON字符串数据。我遇到的问题是,对于较大的消息和不理想的网络条件,使用NetworkStream.DataAvailable似乎不是检测消息结束的可靠方法。似乎在某些情况下,即使对等方仅传输了部分消息(使用TcpClient.GetStream().Write(data, 0, data.Length
),DataAvailable也设置为false。这导致将不完整的数据传递回应用程序,对于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)添加一个拱形的接收超时值,并在读取循环中执行thread.sleep,并在达到接收超时后终止该操作
2)实现某种机制来指示数据有效负载大小-显式地或通过创建元数据头系统来指示应读取多少数据,并在读取大量数据或操作超时后退出
这两个是我能想到的最好的,并且似乎已被其他基于TCP的协议(protocol)(如HTTP)以及其他任何基于RPC的协议(protocol)所验证。
希望这可以节省一些时间。