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

标签 c# sockets tcp

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

我使用 socket.Send() 方法发送 20000 个字符串,并使用执行 socket.Receive() 的循环接收它们。每个字符串都由我用来计算收到的数字的唯一字符分隔(如果你愿意,这就是协议(protocol))。该协议(protocol)是经过验证的,因为即使是碎片化的消息,每个字符串也会被正确计算。在我的本地机器上我得到所有 20000,通过互联网我得到 17000-20000 之间的任何东西。远程计算机的连接速度越慢似乎越糟糕。更令人困惑的是,打开 Wireshark 似乎减少了丢失消息的数量。

首先,这是什么原因造成的?是 TCP/IP 问题还是我的代码有问题?

其次,我该如何解决这个问题?接收所有 20000 个字符串至关重要。

Socket接收代码:

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);
}

套接字发送代码:

private static readonly Encoding encoding = new ASCIIEncoding();
//...
socket.Send(encoding.GetBytes(string));

最佳答案

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

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

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

关于c# - 在 C# 中处理丢弃的 TCP 数据包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1338006/

相关文章:

c# - 获取带有特殊大小写的字符串的前 140 个字符

c# - 注册表中的字体链接

java - ReadableByteChannel.read(ByteBuffer dest) 读取上限为 8KB。为什么?

c++ - 如何使用 BSD 套接字查找 ip 地址?

networking - Labview编程

c# - 我可以使用 Azure 存储队列中的 Response<SendReceipt> 验证消息是否已成功发送

c# - 是否可以使用查询参数来填充 IN 关键字

Java - 从缓冲读取器(从套接字)读取正在暂停线程

java - 通过 TCP 的 DNS 查询

sockets - lua nginx 模块中提到的 TCP 套接字是什么?