【发布时间】:2011-03-16 05:50:51
【问题描述】:
我们目前在同一台 Windows 7 64 位机器上测试客户端和服务器应用程序。它们都是用 C# 编写并使用 P/Invoke 调用 Winsock2 库。
该应用程序总体上运行良好,没有任何错误。 tcp/ip 上每个“跃点”的延迟平均约为 350 微秒。
但是,有时在接收数据包之前会有超过 40 到 50 毫秒的非常长的延迟,然后它们就会突然全部到达。
迄今为止的诊断工作:
在这些延迟接收数据期间,服务器会继续记录它正在发送数据包。它设置为每 1 毫秒发送一次测试数据包,它会在 15 或 20 毫秒内发送一次测试数据包,有时甚至会在客户端收到任何测试数据包之前长达 50 毫秒。
-
tcpdump 用于嗅探环回适配器上的数据包,并显示在此滞后期间,从服务器端口 (6488) 到客户端端口 (61743) 的流量照常。
李> 客户端在循环中调用 select() winsock2 调用,因此在 select() 调用之前通过计数器进行日志记录表明它具有正确的文件描述符。当然,这在延迟前后都可以正常工作。
在 select() 调用之后立即进一步记录显示 fd 不存在 - 这意味着对套接字的读取将阻塞。但是,在没有任何延迟的传输期间,日志显示它按预期工作,因此 select() 返回套接字的 fd 以进行非阻塞读取。
简而言之,环回适配器似乎将这些数据包保存在某个地方很长一段时间,然后才最终将它们传送到接收端。
还有其他想法或解决方案吗?
有些想法是,人们经常声称重叠 I/O 在 Windows 上工作得更好,但如果您需要侦听超过 64 个套接字,这似乎只对可伸缩性很重要。
是不是切换到重叠就可以了?我们希望避免,因为这会增加项目期限和预算。这应该适用于 select() 就好了。
另外,Windows 中处理环回的进程或线程是否可以进行上下文切换或其他什么,如果是这样,有没有办法对其进行配置以避免这些延迟?
编辑:正确的答案是确保禁用 Nagle 算法。我们认为它已被禁用,但这就是发现错误的地方——在我们内部实现的 SetSocketOption() 中,我们使用 GetSocketOption() 来验证。所以事实证明你必须在连接或绑定套接字之前设置 NoDelay,否则它会默默地失效。
非常感谢Fun Mun Pieng的正确答案!!!
【问题讨论】:
-
JOOI,你为什么要 P/invoking Winsock,而不是使用内置的 .NET 网络类?
-
这是由于性能测试比较和不喜欢 .Net 用于异步的线程模型,当线程进入休眠状态时会导致非常昂贵的上下文切换。相反,我构建了一个基于“尾递归”的“纤程”或“延续”风格的任务调度程序,该调度程序共享线程并且从不引起任何上下文切换。主线程在实时处理期间从不休眠。它具有近乎实时的计时器和其他实时功能。它的速度很快,并且可以跨多个内核线性扩展。 .Net 团队已经开始制作其中的一些,但还不是全部。
标签: c# windows-7 tcp winsock2 low-latency