处理C#中丢弃的TCP数据包

Nos*_*ama 7 c# sockets tcp

我在客户端和编写C#的服务器之间一次性发送大量数据.当我在本地计算机上运行客户端和服务器时它工作正常,但是当我将服务器放在互联网上的远程计算机上时,它似乎丢弃了数据.

我使用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, 0, bytesRead);
    CountStringsIn(input);
}
Run Code Online (Sandbox Code Playgroud)

套接字发送代码:

private static readonly Encoding encoding = new ASCIIEncoding();
//...
socket.Send(encoding.GetBytes(string));
Run Code Online (Sandbox Code Playgroud)

Jef*_*ker 4

如果您丢失数据包,您会看到传输延迟,因为它必须重新传输丢失的数据包。这可能非常重要,尽管有一个称为选择性确认的 TCP 选项,如果双方都支持该选项,它将仅触发重新发送那些被丢弃的数据包,而不是自丢弃以来的每个数据包。无法在您的代码中控制它。默认情况下,您始终可以假设每个数据包都按 TCP 顺序传送,如果由于某种原因无法按顺序传送每个数据包,则连接将因超时或连接一端发送RST 数据包。

您所看到的很可能是内格尔算法的结果。它所做的不是在发布数据时发送每一位数据,而是发送一个字节,然后等待来自另一端的确认。在等待时,它会聚合您要发送的所有其他数据,并将其组合成一个大数据包,然后发送。由于 TCP 的最大大小为 65k,因此它可以将相当多的数据合并到一个数据包中,尽管这种情况极不可能发生,特别是因为 Winsock 的默认缓冲区大小约为 10k 左右(我忘记了确切的数量)。此外,如果接收器的最大窗口大小小于 65k,则它只会发送接收器最后通告的窗口大小。窗口大小也会影响 Nagle 算法在发送之前可以聚合的数据量,因为它不能发送超过窗口大小的数据。

您看到这种情况的原因是因为在互联网上,与您的网络不同,第一个确认需要更多时间返回,因此 Naggle 的算法将更多数据聚合到单个数据包中。在本地,返回实际上是瞬时的,因此它能够在您将数据发布到套接字时尽快发送数据。您可以使用 SetSockOpt (winsock) 或 Socket.SetSocketOption (.Net) 在客户端禁用 Naggle 算法,但我强烈建议您不要在套接字上禁用 Naggling,除非您 100% 确定您知道自己在做什么。它的存在是有充分理由的。