当在客户端进行 wireshark 跟踪时,它看起来是这样的:
---SYN--->
<-SYN-ACK-
---ACK--->
-HTTPGET->
但是,当在服务器端对相同流量进行 wireshark 跟踪时,它看起来是这样的:
---SYN--->
<-SYN-ACK-
-HTTPGET->
其中 HTTPGET 数据包似乎是 SYN-ACK 的 ACK,因为它具有与前一个 ACK 数据包相同的序列和确认号。
稍后以相反的方式发生了同样的事情:
当在服务器端进行 wireshark 跟踪时,它看起来是这样的:
-HTTPGET->
<---ACK---
<-200-OK--
但在客户端对相同流量进行的 wireshark 跟踪显示如下:
-HTTPGET->
<-200-OK--
200OK 似乎是 HTTPGET 的 ACK,因为它具有与前一个 ACK 数据包相同的序列和确认号。
所以我的问题是,是否有任何已知的网络元素以中间人身份进行 TCP 搭载?
我相信这会给双方带来一些问题,因为他们似乎一直在等待“丢失”的 ACK:例如:
*服务器端对 200OK 进行重复 ACK,因为它正在等待 TCP 三次握手中的第三个 ACK(该 ACK 已被搭载到后续的 HTTPGET 中)。
*由于原始的非捎带 ACK 从未到达,因此客户端重新传输 HTTPGET。
以下是样本踪迹:http://goo.gl/7V0pah
答案1
在查看了传输过程中数据包发生的大量变化后,我终于发现了一个可以合理解释为什么客户端认为来自服务器的数据包不按顺序的变化。
查看时间戳选项,特别是查看从服务器到客户端的数据包上的时间戳值字段。从服务器开始,这些值从 SYN-ACK 中的 7236650 到最后一个数据包中的 7246570。
但是在客户端接收时,SYN-ACK 数据包中的时间戳值已被修改为值 1068916716。其余数据包从服务器发送到客户端时时间戳值均未修改。
因此,从客户端的角度来看,时间戳从 1068916716 变为 7236708。换句话说,它是倒退的,这当然是客户端认为数据包乱序的正当理由。
因此,我猜测问题的根本原因是 SYN-ACK 中的时间戳被破坏了。
在得出这个猜测之前,我从数据包跟踪中观察到了许多其他有趣的事实。虽然那些其他数据点无法解释您的问题,但它们仍可能是进一步调查的相关数据点。我使用 Wireshark 检查数据包捕获,但任何可以解码所有 TCP 标头的工具都可以。
- 正在进行一些 NAT。来自客户端的数据包源自源 IP 192.168.43.87,到达源 IP 10.53.72.193。由于在两个 RFC 1918 范围之间进行 NAT 并不常见,我猜想发生了两层 NAT。我猜想 192.168.43.87 被映射到靠近客户端的公共 IP,然后映射到靠近服务器的 10.53.72.193。(随着修改的进行,客户端端口号保持不变有点令人惊讶。)
- 仔细观察从客户端到服务器的数据包上的 IPID 字段,我观察到:它们从 开始
0x5b1f
,然后从该值开始增加,直到达到0x51d3
最后一个数据包。然而,在接收端,第一个数据包到达时具有不同的 IPID。它是0xd5f5
。我还注意到 ID 的数据包0x51c2
被延迟,并在 ID 的数据包之后到达0x51c4
。 - IPID 字段被破坏的 SYN 数据包的选项也被重新排序。这不会造成任何问题,但表明中间件已将此 SYN 数据包完全拆开,并生成了一个在大多数方面与原始数据包相似的新数据包。我们还可以看到,对于这个特定的数据包,TTL 在传输过程中增加了。
- 无论是 IPID 错误、ACK 数据包丢失还是延迟重传,都没有造成任何问题。服务器确实回复了请求,而延迟的数据包没有任何意义,因为它是服务器已经收到的数据包的重传。
- 来自服务器的数据包上的 IPID 从
0x0000
SYN-ACK 数据包开始,然后从0x2766
第二个数据包到0x2770
最后一个数据包。 - 只有带ID的数据包
0x2766
丢失了。其余ID按顺序到达客户端。 - 窗口缩放选项在飞行过程中被修改。服务器发送了 9,客户端收到了 7。
- 丢失的数据包是请求本身的 ACK。这个数据包丢失解释了为什么客户端需要重发请求。
- 服务器处理请求并产生回复的时间超过 200 毫秒。延迟 ACK 这么长时间是不可接受的。为了能够将其搭载在有效载荷数据包上,短暂延迟可能是可以的。但如果几毫秒内没有出现有效载荷数据包,则应该自行传输 ACK。
- 在收到包含 HTTP 回复第一部分(和请求的 ACK)的数据包(即 ID 为 的数据包)后
0x2767
,客户端将响应初始数据包的序列号的 ACK,SYN-ACK
而不是它刚收到的数据包的序列号。并且它会忽略收到的 ACK 并继续重新传输请求。换句话说,客户端的行为就像是刚收到一个无序的数据包一样。如果客户端这样做只是因为服务器到客户端的 ACK 丢失,那么显然是客户端的 TCP 堆栈中存在错误。我怀疑这个数据包还有其他问题。在从服务器到客户端的传输过程中,这个数据包上公布的窗口大小增加了。但是由于窗口远未满,所以这不太可能是问题所在。 - 仔细观察绝对序列号(而不仅仅是 Wireshark 中默认显示的相对值),我注意到序列号在从客户端到服务器的传输过程中未经修改,但在从服务器到客户端的传输过程中进行了修改。只要在从客户端到服务器的传输过程中对已确认的序列号进行了相应的修改,就不会造成任何问题。
当在 ICMP 错误消息中发现错误时,所涉及的中间件无法调整所有更改。因此,我相信在发送数据包之前有选择地减少数据包的 TTL 可用于识别沿途哪个路由器正在破坏数据包。(除非提供商决定丢弃所有相关的 ICMP 数据包,这样您将无法调试问题,如果是这种情况,是时候寻找另一个提供商了。)