我正在分析客户端和在 HP 刀片服务器上运行的 Linux 网络服务器之间的流量,当网络服务器关闭连接时,客户端有时会卡在等待更多数据的状态。
Web 服务器运行 apache2,出于某种原因,它选择运行带有连接关闭的 HTTP/1.1,而不是允许客户端在同一个连接上发送多个请求并关闭连接,就像标准的 HTTP/1.1 一样(那是另一个故事...但它给服务器留下了数千个 TIME_WAIT 套接字,而不是将该状态推送给客户端)...
无论如何,有时 HTTP 请求会中断,但仍然不知道它到底在哪里中断。在服务器端,一切看起来都很好,只是客户端开始在确认之间发送大量 RST 数据包。
我从 Web 服务器和客户端经过的 NAT 中捕获了 tcpdump 数据,如果不是 Web 服务器上出现非常奇怪的行为,我会怀疑是 NAT 造成的。
当 Web 服务器处理 HTTP GET 请求时,第一个传出的数据包在 IP 有效负载中为 2960 字节,在线路中为 2974 字节。这非常奇怪,因为在 NAT 中的客户端,客户端会收到两个 1514 字节的数据包,其中 TCP 有效负载为 1460 字节。
离开 Web 服务器接口的下一个和即将发送的数据包使用 MTU 内的有效载荷 1460(线路上为 1514)。
我相信位于 Web 服务器和网络之间的 (Cisco) SLB 中一定存在某种神奇之处,因此 2960 的第一个 DF 数据包会被挤过 SLB,并通过某种先进的 L3 拦截在 SLB 中神奇地被分割。
Q1)为什么 apache webserver/tcp 堆栈甚至会尝试在 MTU 设置为 1500 的接口上推送 2960 字节的数据包?
Q2) 它如何通过网络以两个数据包的形式到达客户端?
Q3) 由于所有数据包都设置了 DF 位,因此 Web 服务器如何知道 MTU 应该减小到 1460,即使没有收到设置了“需要分片”的 ICMP。
不要问我为什么要问这些问题,我只是一个大型组织里的人,试图理解为什么事情有时行不通。
我有一些有趣的 tcpdump 日志,如果需要的话可以发布,我只需要替换公共 IP 地址等...
答案1
如果您在服务器上捕获数据包,那么您可能会看到 TCP 发送的数据包大于 MTU。但是,线路上的数据包将仅为 MTU 大小。您可以通过在网络设备(交换机)等上捕获数据包来验证这一点。或者,在远程(客户端)计算机上捕获数据包将显示每个数据包 <= MTU。
这种行为是由于启用 TSO/GSO 后,TCP 段会被 NIC 硬件分割成 MTU 大小的数据包。由于 tcpdump 在软件层进行捕获,因此它会看到大于 MTU 的段被发送到 NIC 卡进行进一步传输。
如果您禁用 NIC 的 tso/gso,那么您将看到所有传出的数据包都 <= MTU 大小(更可能是 pMTU 大小)。
答案2
Q1:我不认为 Apache 知道它在那里做什么。它将处理 TCP 连接,其余部分留给操作系统 TCP 堆栈;)
Q2:碎片。数据包在途中被拒绝,然后发回“再次发送,更小的数据包”,服务器(不是 apache - 这是 ip 堆栈)再次发送更小的数据包。
Q3:不是。真的,我再一次认为,apache 根本不在较低级别处理 tcp 堆栈,而 MTU 等则要低得多。服务器的 TCP 堆栈负责此操作,并且如果设置了正确的设置(不仅仅是“需要分段”,而且还要设置正确的较小尺寸 - 您查看的参数是 TCP MSS)。
从技术上讲,这看起来像是某些损坏的设备和/或某些损坏的 TCP 实现,因为 SYN 数据包上的 MSS 参数似乎包含大于允许的大小,或者发送方计算机只是忽略了 MSS 值。
http://en.wikipedia.org/wiki/Maximum_segment_size是一个很好的入门参考。似乎 MTU 发现失败(或结果被忽略),然后使用非标准大小。