我使用socket.Send()方法发送20000个字符串,并使用一个执行socket.Receive()的循环接收它们.每个字符串由唯一字符分隔,我用它来计算收到的数字(如果你愿意,这是协议).该协议经过验证,即使使用碎片消息,每个字符串也会被正确计算.在我的本地机器上,我得到全部20000,通过互联网我得到17000-20000之间的任何东西.远程计算机的连接速度似乎更慢.为了增加混乱,打开Wireshark似乎减少了丢弃的消息数量.
首先,是什么原因造成的?这是TCP / IP问题还是我的代码有问题?
其次,我怎么能绕过这个?接收所有20000个字符串至关重要.
private static readonly Encoding encoding = new ASCIIEncoding(); ///... while (socket.Connected) { byte[] recvBuffer = new byte[1024]; int bytesRead = 0; try { bytesRead = socket.Receive(recvBuffer); } catch (SocketException e) { if (! socket.Connected) { return; } } string input = encoding.GetString(recvBuffer,bytesRead); CountStringsIn(input); }
private static readonly Encoding encoding = new ASCIIEncoding(); //... socket.Send(encoding.GetBytes(string));
解决方法
您所看到的很可能是Nagle算法的结果.它所做的不是在发布时发送每一位数据,而是发送一个字节,然后等待来自另一方的ack.在等待时,它会聚合您要发送的所有其他数据,并将其合并为一个大数据包然后发送.由于TCP的最大大小为65k,它可以将相当多的数据合并到一个数据包中,尽管这种情况发生的可能性极小,特别是因为winsock的默认缓冲区大小约为10k左右(我忘记了确切的数量).此外,如果接收器的最大窗口大小小于65k,它将仅发送与接收器的最后一个广告窗口大小一样多的数量.窗口大小也影响Nagle的算法,因为它在发送之前可以聚合多少数据,因为它不能发送超过窗口大小的数据.
您之所以看到这一点,是因为在互联网上,与您的网络不同,第一个ack需要更多时间才能返回,因此Naggle算法会将更多数据聚合到一个数据包中.在本地,返回实际上是瞬时的,所以它能够尽快发送数据到将其发布到套接字.您可以使用SetSockOpt(winsock)或Socket.SetSocketOption(.Net)在客户端禁用Naggle算法,但我强烈建议您不要在套接字上禁用Naggling,除非您100%确定您知道自己在做什么.这是有充分理由的.