我正在编写一个 C 程序,它通过 TCP 连接发送 bash shell 的输出。为了使我的程序响应更快,我使用setsockopt()来启用TCP_NODELAY,这会禁用Nagle的缓冲算法。这非常有效,除了大消息很少出现延迟之外。例如,如果消息超过 500 字节左右(可能是 512)。前 500 个字节将通过(在小消息中快速),然后在一次性接收其余字节之前将有 1-2 秒的延迟。这种情况只会在每 10-15 次收到大消息时发生一次。在服务器端,消息一次一个字节地写入套接字,并且所有字节都可用,因此这种行为对我来说是意外的。
我最好的猜测是套接字中某个地方有一个 512 字节的缓冲区导致了阻塞?我做了一些时间测试来查看滞后在哪里,我很确定是套接字本身发生了滞后。服务器端的所有数据都无阻塞地写入,但客户端在延迟后收到消息结束。不过,我使用 getsockopt() 来查找套接字的接收和发送缓冲区,它们远远超过 512 字节 - 分别为 66000 和 130000。在客户端,我使用 Express js 在处理程序中接收数据(app.on('data', function(){}))。但我读到这个express函数不缓冲数据?
有人能猜出为什么会发生这种情况吗?谢谢!
最佳答案
由于 TCP_NODELAY 意味着尽快将每段数据作为数据包发送,而不将数据组合在一起,因此听起来您正在发送大量数据包。由于您一次写入一个字节,因此它可以发送仅包含一个字节有效负载和一个更大的帧的数据包。这在大多数情况下都可以正常工作,但一旦第一个数据包由于某种原因丢失,接收方就需要在 TCP 套接字上进入纠错模式,以请求重新传输丢失的数据包。这将导致至少一次甚至多次往返延迟。听起来你对前几百个数据包(值(value) 500 个字节)很幸运,然后通常会遇到第一个数据包丢失,并由于纠错而减慢速度。一种简单的解决方案可能是写入更大的 block ,例如一次写入 10 个字节,而不是 1 个字节,这样丢弃数据包的机会就会少得多。那么您可能会像只处理 5000 字节左右的消息时一样频繁地看到此问题。一般来说,设置 TCP_NODELAY 会导致事情一开始进展得更快,但最终会更快地到达第一个丢弃的数据包,因为 TCP_NODELAY 不会减少每单位数据量发送的数据包数量。因此,它会增加或保持数据包数量不变,这意味着在一定数据量内遇到丢弃数据包的机会将会增加。这里需要在交互感觉和第一次打嗝之间进行权衡。通过避免 TCP_NODELAY,您可以延迟在第一次错误重传发生之前发送的典型数据量。
关于c - 使用 tcp_nodelay 发送大消息时出现奇怪的延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31323509/