TCP延迟确认的解决方法是什么?我正在发布一个在线(基于网格)视频游戏,该游戏使用TCP协议来确保服务器-客户端网络拓扑中的可靠通信。我的游戏运行良好,但延迟高于预期(类似的TCP游戏似乎在将延迟降至最低方面做得更好)。在调查过程中,我发现运行MicrosoftWindows的客户端(而不是MacOSX客户端)的延迟只是出乎意料的高。此外,我发现如果Windows客户端在注册表中设置TcpAckFrequency=1并重新启动计算机,它们的延迟就会变得正常。看来我的网络设计没有考虑延迟确认:在不考虑延迟确认的情况下,Nagle算法和Winsock缓冲的交互设计会极大地影响性能。(http://support.microsoft.com/kb/214397)但是,我发现几乎不可能在我的游戏(或与此相关的任何游戏)中解释延迟确认。根据MSDN,MicrosoftTCP堆栈使用以下标准来决定何时对接收到的数据包发送ACK:(http://support.microsoft.com/kb/214397)阅读本文,可以假设MicrosoftTCPstackdelaysacknowledgments解决方案如下:禁用Nagle算法(TCP_NODELAY)。禁用套接字的发送缓冲区(SO_SNDBUF=0),以便可以进行发送调用以发送数据包。调用send时,再次调用send并返回一个字节的数据,如果预计不会立即发送更多数据,则该数据将被接收方丢弃。使用这种方法,接收器将在与前一个数据包大致相同的时间接收到第二个数据包。因此,ACK应该立即从接收方发送到发送方(模拟TcpAckFrequency=1在注册表中的作用)。然而,根据我的测试,这种延迟只改善了注册表编辑所做的大约一半。我错过了什么?问:为什么不使用UDP?A:我选择TCP是因为我发送的每个数据包都需要到达(并且是有序的);如果它丢失(或出现故障),则没有数据包值得重新传输。仅当数据包可以丢弃/乱序时,UDP才比TCP快!从WindowsVista开始,TCP_NODELAY选项必须在调用connect之前设置,或者(在服务器上)在调用listen之前设置。如果在调用connect后设置TCP_NODELAY,它实际上不会禁用Nagle的算法,但GetSocketOption会声明Nagle已禁用!这一切似乎都没有记录,并且与许多关于该主题的教程/文章相矛盾。Nagle被有效禁用,TCP延迟确认不再导致延迟。你应该没什么可做的了。您建议的所有解决方法都有助于那些未正确设计来处理TCP的协议。大概您的协议设计用于在TCP上工作,对吧?您的问题几乎可以肯定是一个或两个:您正在使用少量数据调用TCP发送函数,即使没有理由不能使用更大的数据块调用它。您没有实现应用程序协议数据单元的应用程序级确认。实施这些以便ACK可以搭载它们。使用这种方法,接收器将在与前一个数据包大致相同的时间接收到第二个数据包。因此,ACK应该立即从接收方发送到发送方(模拟TcpAckFrequency=1在注册表中的作用)。我不相信这总是会导致发送第二个单独的数据包。我知道您有Nagle的禁用和零发送缓冲区,但我看到了StrangerThings。一些wireshark转储可能会有所帮助。一个想法:发送完整的MSS数据(在1500-MTU网络上通常为1460字节),而不是您的“金丝雀”数据包只有一个字节。使用可靠的UDP库并编写您自己的拥塞控制算法,这肯定会克服您的TCP延迟问题。以下库用于可靠的UDP传输:http://udt.sourceforge.net/要解决此问题,有必要了解TCP连接的正常运行。Telnet是一个很好的分析例子。TCP通过确认成功的数据传输来保证交付。“Ack”本身可以作为消息发送,但这会引入相当多的开销——Ack本身是非常小的消息,但较低级别的协议会添加额外的标头。出于这个原因,TCP倾向于在它发送的另一个数据包之上搭载Ack消息。通过Telnet查看交互式shell提供了稳定的击键和响应流。如果打字暂停一小段时间,屏幕上就没有回声。唯一的时间流程停止是如果输出没有相应的输入。但是因为你只能这么快地读取,所以等待几百毫秒看看是否有一个按键可以搭载在Ack上是可以的。因此,总而言之,我们在双向都有稳定的数据包流,而Acks通常是搭载的。如果由于应用原因造成流量中断,则不会发现DelayedAcks。回到您的协议:您显然没有请求/响应协议。这意味着Ack不能被搭载(问题1)。虽然接收操作系统将发送单独的Ack,但它不会向它们发送垃圾邮件。使用TCP_NODELAY和发送(Windows)端的两个数据包的解决方法假定接收端也是Windows,或者至少表现得如此。这是一厢情愿的想法,而不是工程。其他操作系统可能决定等待三个数据包发送Ack,这完全打破了您使用TCP_NODELAY来强制一个额外的数据包。“等待3个数据包”只是一个例子;还有许多其他有效的算法可以防止Ack垃圾邮件被您的第二个单字节虚拟数据包欺骗。什么是真正的解决方案?响应在协议级别发送。无论操作系统如何,它都会在协议响应上搭载TCPAck。反过来,这个响应也会在另一个方向强制一个Ack(响应也是一个TCP消息)但是你不关心响应的延迟。响应是接收操作系统搭载第一个Ack。我建议你保留Nagle的算法和缓冲区,因为它的基本目的是收集小写到完整/更大的数据包(这将大大提高性能),但同时使用FlushFileBuffers()在套接字完成发送后发送一段时间。我在这里假设,你的游戏有一些处理东西的主循环,然后在进入下一轮之前等待一段时间:睡眠(20-time_spent_processing);};()在插入FlushFileBuffers()之前调用:while(run_my_game){process_game_events_and_send_data_over_network();FlushFileBuffers(我的套接字);睡眠(20-time_spent_processing);};圆形的。您应该从Nagel的算法中获得性能优势并最大限度地减少延迟。如果这不起作用,那么如果您发布一些(伪)代码来解释您的程序实际如何工作将会很有帮助。编辑:当我再次考虑您的问题时,我又想到了两件事:a)延迟的ACK数据包实际上不会造成任何延迟,因为它们的传输方向与您发送的数据相反。在最坏的情况下,它们会阻塞发送队列。但是,当连接带宽和内存限制允许时,TCP将在几个数据包后解决此问题。所以除非你的机器有非常低的RAM(对于更大的发送队列来说不够),或者你实际上传输的数据比你的连接允许的多,延迟的ACK缓冲区是一种实际上会提高性能的优化。b)您正在使用专用线程进行发送。我想知道为什么。AFAIK是SocketAPI多线程安全的,因此每个产品线程都可以自己发送数据-除非您的应用程序需要这样的队列,否则我建议也删除这个专用的发送线程,这可能会导致额外的同步开销和延迟。我在这里特别提到延迟。由于操作系统可能决定不立即安排发送线程再次执行,而它在队列中是畅通的。典型的重新调度延迟在10毫秒范围内,但在负载下它们可能会突破50毫秒或更多。作为工作场所,您可以尝试摆弄日程安排的优先级。但它不会减少操作系统本身的延迟。顺便一提。您可以轻松地对TCP和您的网络进行基准测试,只需在客户端上有一个线程,在服务器上有一个线程,使用一些数据ping/pong。我发送的每个数据包都需要到达(并且按顺序);这个要求是造成延迟的原因。要么你有一个数据包丢失可以忽略不计的网络,UDP将传输每个数据包,要么你有丢失,TCP正在重传,延迟(重复)所有重传间隔(至少往返时间)。这个延迟是不一致的,因为它是由丢包触发的;抖动通常比数据包快速合并导致的可预测确认延迟更糟糕!这是一个很容易做出的假设,但却是错误的。除了ARQ之外,还有其他方法可以提高丢弃率并因此提供较低的延迟:前向纠错方法可以改善丢弃恢复的延迟,但需要额外的带宽。以上是C#学习教程:TCP延时确认的解决方法是什么?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
