我有一个应用程序,使用TCPClient和它的底层NetworkStream通过TCP套接字发送以换行符结尾的消息。
实时数据流每100毫秒传输大约28k的数据进行监测。
我已经剥离了不相关的代码,以下是我们读取数据的基本方法:
TcpClient socket; // initialized elsewhere
byte[] bigbuffer = new byte[0x1000000];
socket.ReceiveBufferSize = 0x1000000;
NetworkStream ns = socket.GetStream();
int end = 0;
int sizeToRead = 0x1000000;
while (true)
{
bytesRead = ns.Read(bigbuffer, end, sizeToRead);
sizeToRead -= bytesRead;
end += bytesRead;
// check for newline in read buffer, and if found, slice it up, and return
// data for deserialization in another thread
// circular buffer
if (sizeToRead == 0)
{
sizeToRead = 0x1000000;
end = 0;
}
}
我们看到的症状有点不稳定,这取决于我们发送回来的数据量,存在一种记录“滞后”的情况,其中我们从流中读取的数据与我们传递的数据相比逐渐变得越来越旧(在几分钟的流媒体后,“滞后”达到数十秒的数量级),直到最终所有数据一次性全部抓住,然后循环重复。
我们通过将sizeToRead最大化来解决问题,并且(无论是否需要,我们都这样做了),删除了TcpClient上设置的ReceiveBufferSize,并保持其默认值8192(仅更改ReceiveBufferSize无法纠正它)。
int sizeForThisRead = sizeToRead > 8192 ? 8192 : sizeToRead;
bytesRead = ns.Read(bigBuffer, end, sizeForThisRead);
我认为可能是与Nagle和延迟ACK的交互有关,但wireshark显示根据时间戳和查看数据(其中包含时间戳,并且服务器和客户端时钟在一秒内同步)数据正常到达。
我们在ns.Read之后输出日志,确保问题出现在Read调用而不是反序列化代码中。
所以我的想法是,如果你将TcpClient的ReceiveBufferSize设置得非常大,并且在其基础的NetworkStream的Read调用中传递bytesToRead要比预期到达的字节数多得多,则在Read调用中会发生超时等待这些字节到达,但它仍然无法返回流中的所有内容?这个循环中的每个连续调用都会超时,直到1兆字节缓冲区已满,在end重新设置为0后,它会吸入流中剩余的所有内容,导致它们全部赶上——但是它不应该这样做,因为在下一个迭代中它应该完全清空流(因为下一个sizeToRead仍然大于缓冲区中可用的数据)。
或者也许有些事情我没有考虑到而不能综合考虑-但也许这里聪明的人可以想到一些东西。
或者也许这是预期的行为-如果是这样,为什么?