在C#中处理丢弃的TCP数据包 [英] Handling dropped TCP packets in C#

查看:89
本文介绍了在C#中处理丢弃的TCP数据包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在客户端和服务器之间用C#一口气发送大量数据.当我在本地计算机上运行客户端和服务器时,它工作正常,但是当我将服务器放在Internet上的远程计算机上时,似乎会丢失数据.

I'm sending a large amount of data in one go between a client and server written C#. It works fine when I run the client and server on my local machine but when I put the server on a remote computer on the internet it seems to drop data.

我使用socket.Send()方法发送20000个字符串,并使用执行socket.Receive()的循环接收它们.每个字符串都由唯一的字符分隔,我用这些字符来计算收到的数字(如果您愿意,这是协议).该协议得到了证明,因为即使有零散的消息,每个字符串也可以正确计数.在我的本地计算机上,我得到了20000,在Internet上我得到了17000-20000之间的任何东西.远程计算机的连接速度较慢似乎更糟.更令人困惑的是,启用Wireshark似乎可以减少丢弃的消息的数量.

I send 20000 strings using the socket.Send() method and receive them using a loop which does socket.Receive(). Each string is delimited by unique characters which I use to count the number received (this is the protocol if you like). The protocol is proven, in that even with fragmented messages each string is correctly counted. On my local machine I get all 20000, over the internet I get anything between 17000-20000. It seems to be worse the slower connection that the remote computer has. To add to the confusion, turning on Wireshark seems to reduce the number of dropped messages.

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

First of all, what is causing this? Is it a TCP/IP issue or something wrong with my code?

第二,我该如何解决?接收所有20000个字符串至关重要.

Secondly, how can I get round this? Receiving all of the 20000 strings is vital.

套接字接收代码:

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顺序传送的,并且如果出于某种原因不能按顺序传送每个数据包,则连接会由于超时或连接的一端而断开,从而发送一个TCP消息.RST数据包.

If you're dropping packets, you'll see a delay in transmission since it has to re-transmit the dropped packets. This could be very significant although there's a TCP option called selective acknowledgement which, if supported by both sides, it will trigger a resend of only those packets which were dropped and not every packet since the dropped one. There's no way to control that in your code. By default, you can always assume that every packet is delivered in order for TCP and if there's some reason that it can't deliver every packet in order, the connection will drop, either by a timeout or by one end of the connetion sending a RST packet.

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

What you're seeing is most likely the result of Nagle's algorithm. What it does is instead of sending each bit of data as you post it, it sends one byte and then waits for an ack from the other side. While it's waiting, it aggregates all the other data that you want to send and combines it into one big packet and then sends it. Since the max size for TCP is 65k, it can combine quite a bit of data into one packet, although it's extremely unlikely that this will occur, particularly since winsock's default buffer size is about 10k or so (I forget the exact amount). Additionally, if the max window size of the receiver is less than 65k, it will only send as much as the last advertised window size of the receiver. The window size also affects Nagle's algorithm as well in terms of how much data it can aggregate prior to sending because it can't send more than the window size.

之所以会这样,是因为在互联网上,与您的网络不同,第一次确认需要花费更多的时间才能返回,因此Naggle的算法会将更多的数据汇总到一个数据包中.在本地,返回实际上是瞬时的,因此它能够像将数据发布到套接字一样快地发送数据.您可以使用SetSockOpt(winsock)或Socket.SetSocketOption(.Net)在客户端禁用Naggle的算法,但我强烈建议您不要在套接字上禁用Naggling,除非您100%确信您知道自己在做什么.在那里是有很好的理由的.

The reason you see this is because on the internet, unlike your network, that first ack takes more time to return so Naggle's algorithm aggregates more of your data into a single packet. Locally, the return is effectively instantaneous so it's able to send your data as quickly as you can post it to the socket. You can disable Naggle's algorithm on the client side by using SetSockOpt (winsock) or Socket.SetSocketOption (.Net) but I highly recommend that you DO NOT disable Naggling on the socket unless you are 100% sure you know what you're doing. It's there for a very good reason.

这篇关于在C#中处理丢弃的TCP数据包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆