当我们尝试从 Web 服务器获取文件时,有时无法访问。使用 tcpdump 检查通信后,我们发现源计算机随机选择源端口进行通信。这应该没问题,但“随机选择”并不像想象的那样随机。因此,源端口通常会在短时间后重新使用(有时只有 2 秒)。在目标系统上,套接字处于 TIME_WAIT 状态,因此目标会断开通信。问题是,为什么服务器选择的源端口不够随机(如 3388、3345、2345、3388)。
当使用带有以下参数的 wget 时,我们能够重新创建完全相同的行为:
wget -t 1 "xxx/test.html" -O /dev/null -o /dev/null -d --bind-address=yyy.yyy.yyy.yyy
然后系统会随机选择端口,但不是那么随机,如果端口重用时间太短,则无法进行通信(重新发送 SYN 数据包)
对 wget 语句进行一点修改:
wget -t 1 "xxx/test.html" -O /dev/null -o /dev/null -d
一切运行正常,端口逐一选择。通信从未中断。
因此,我们的源系统的工作方式与第一个 wget 语句完全相同,但我们需要它像第二个一样工作,或者更随机地选择端口。如何更改 TCP 通信的选择源端口行为?
答案1
我已经在本地测试过此行为,但无法复制此行为。即使我将 Linux 计算机设置为仅使用两个本地端口,我也能够毫无问题地启动到同一域(内部或外部)的多个(20 个以上)连接。我还通过设置 no-http-keep-alive 标头(并通过 netstat 确认)来确保远程服务器处于 TIME-WAIT 状态。
我发现,当使用--bind-address
WGET 的标志时,它使用 Bind() 创建套接字,其中源 IP 是命令行上指定的任何 IP,源端口是从中选择的/proc/sys/net/ipv4/ip_local_port_range
。每当需要选择新的源端口时,这将是前一个端口 + 2。到达源端口范围的末尾后,它将回到起点并再次以 2 为单位开始计数。
当您省略该--bind-address
标志时,Connect() 用于建立套接字,其中源 IP 来自默认接口,源端口是范围内的随机端口/proc/sys/net/ipv4/ip_local_port_range
。新的源端口也将从此范围中随机选择。
在任何一种情况下,当套接字被重用时,内部 TCP 时钟都会增加初始序列号,从而允许服务器接受新的连接。这与我对使用相同源端口和增加 ISN 值的 20 个连接请求的测试结果一致 - 所有请求均在 TIME-WAIT 状态下被服务器接受。
如果我们排除 tcp_tw_recycle 引入的任何异常情况,因为我不相信 Windows 服务器上存在该选项,我能想到的唯一其他事情就是以下组合:
- 在您的 Linux 服务器上配置一个小的临时端口范围,这会导致在使用时定期重复使用本地端口
--bind-interface
。 - 客户端和服务器之间的防火墙或其他网络设备正在执行 TCP 序列号随机化。因此,不能指望到达服务器的初始序列号会大于上一个连接,并且可能会导致连接中断。