我有一个使用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)所验证。

希望这可以节省一些时间。

09-06 03:31