TCP服务器发送[ACK],然后是[PSH,ACK]。

5
我正在开发一个高性能的TCP服务器,当我使用TCP客户端进行高流量传输时,有时会看到服务器无法快速处理。经过仔细检查,我发现TCP服务器中存在“时间差”峰值。我还看到服务器发送了一个ACK,0.8秒后发送相同seqno的PSH,ACK。在pcap中,我看到了这种模式出现了多次。请专家们评论一下为什么服务器会发送一个ACK,然后在之后延迟一段时间再发送PSH,ACK?
TCP服务器的PCAP:链接

听起来实际问题是当我使用TCP客户端进行高流量传输时,我会看到服务器时而无法快速处理。虽然我建议您添加更多细节,但Stack Overflow专注于编程问题,因此如果您是网络专业人员,应该在Server Fault或networkengineering.stackexchange.com上提出这个扩展问题。 - Ross Jacobs
在第95182帧中,客户端发送了1244字节的数据,序列号为87918542。在第95183帧中,服务器确认了87918542+1244=87919786。接下来,在第95184帧中,服务器发送了315字节的数据,并且由于除了初始SYN之外的每个段都设置了ACK标志,因此它将seq填充为87919786,因为它没有从客户端收到任何其他数据。PSH标志是向客户端TCP堆栈传递数据的提示,建议315字节是服务器通过套接字进行的单个写操作。 - JimD.
@JimD。感谢您的详细分析,它有助于解决我一些疑问。所以,如果我理解正确,在帧95183和95184之间的0.8秒“delta time”将表明客户端没有向服务器发送任何数据,对吗?在这种情况下,客户端具有忙循环来写入SSL套接字。 - user2548514
请注意,帧95182不是完整的帧,并且已设置PSH,因此这很可能是写入套接字的完整有效负载。 95182中的数据在95183中得到确认。然后我们在捕获中看不到0.8秒的任何数据,然后服务器回复513个字节。如果客户端处于忙循环写入套接字状态,为什么在0.8秒内没有看到更多数据? recvq中有足够的空间,并且从捕获中没有明显的原因说明它不会继续发送。 - JimD.
@JimD。非常好的观点!服务器上的GC暂停会导致这个问题吗?或者您认为这肯定表明客户端没有向套接字写入任何内容。 - user2548514
服务器上的应用程序垃圾回收对TCP堆栈没有任何影响,除了它不会处理来自recvq的数据,因此recvq可能会填满。然而,广告窗口为647680,因此有足够的空间,而且没有明显的原因客户端不能发送。在众多可能的解释中,客户端的GC是一个可能的解释。 - JimD.
1个回答

6
为了简化对 ACKPSH 的理解:
  • ACK 始终存在,它仅告知客户端服务器最后接收的字节。
  • PSH 告诉客户端/服务器将字节推送到应用层(这些字节形成一个完整的消息)。
通常情况下,以下是您熟悉的过程:
  1. 操作系统有缓冲区,用于存储从客户端接收的数据。
  2. 一旦接收到数据包,就将其添加到缓冲区中。
  3. 应用程序调用套接字接收方法,并将数据从缓冲区中取出。
  4. 应用程序将数据写回套接字(响应)。
  5. 操作系统使用标志 PSH,ACK 发送数据包。
现在想象以下这些场景:
  • 步骤4没有发生(应用程序没有将数据写回,或者需要很长时间才能写回)

    => 操作系统仅使用 ACK 确认接收(数据包中将不会有任何数据),如果应用程序稍后决定发送内容,则会使用 PSH,ACK 发送。

  • 服务器发送的消息/数据太大,无法放入一个数据包中:

    • 第一个数据包将不具有 PSH 标志,并且仅具有 ACK 标志。
    • 最后一个数据包将具有标志 PSH,ACK,以通知消息的结束。

1
谢谢你的回答!你能否澄清一下接下来的问题,以免出现类似的问题。第一个问题,我是否正确理解包含消息最后一部分的tcp段总是设置了PSH标志?我知道有时候会设置PSH标志,但是消息并没有在最后一个段中完成。第二个问题,是否存在这样一种情况,即最后接收到的段包含前一个消息的一部分和新消息的字节?提前致谢。 - slinkin
2
TCP段中包含消息的最后一部分总是设置了PSH标志吗?是的。 - Ayoub Kaanich
2
在实际应用中,是否存在最后接收的数据段包含前一条消息的部分和新消息的字节的情况?我没有见过这种情况发生。 - Ayoub Kaanich
1
我对第二个问题不确定,因为在章节2.8数据通信(rfc793)中有这样的描述:“任何特定段中的数据可以是单个SEND调用的结果(全部或部分),也可以是多个SEND调用的结果。” 我是否正确理解,一个段可以涵盖以下情况:{>=1消息},{>=1消息,新消息的一部分},{前一消息的一部分,>=1新消息,新消息的一部分}。 - slinkin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接