问题背景
在生产环境中,我们的 Windows Server 2016 服务器出现了大量的 TIME_WAIT 连接,导致可用端口不足,新的连接请求被拒绝。通过 netstat -ano查看,发现 TIME_WAIT 状态的连接数非常多。
什么是 TIME_WAIT
TIME_WAIT 是 TCP 连接断开过程中的一个正常状态。当主动关闭连接的一方发送最后一个 ACK 后,会进入 TIME_WAIT 状态,默认持续 2MSL(Maximum Segment Lifetime)时间,以确保:
- 远端正确接收到最后的 ACK
- 网络中延迟的数据包不会影响新连接
正常情况下,TIME_WAIT 会在一段时间后自动释放。但在高并发场景下,如果连接建立和关闭频繁,就会产生大量 TIME_WAIT 连接。
监控脚本编写
为了实时监控 TIME_WAIT 连接的情况,我编写了一个 PowerShell 脚本来持续跟踪和记录。
完整监控脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
param( [int]$IntervalSeconds = 60, [int]$Threshold = 100 )
$baseLogDir = "C:\Temp\TimeWaitMonitor" if (!(Test-Path $baseLogDir)) { New-Item -Path $baseLogDir -ItemType Directory | Out-Null }
while ($true) { $date = (Get-Date).ToString("yyyy-MM-dd") $time = (Get-Date).ToString("HH:mm:ss") $logFile = Join-Path $baseLogDir "TimeWaitMonitor_$date.log" $csvFile = Join-Path $baseLogDir "TimeWaitTrend_$date.csv"
if (!(Test-Path $csvFile)) { "Time,Count" | Out-File -Encoding UTF8 $csvFile }
$timeWaitLines = netstat -ano | findstr /c:"TIME_WAIT" $timeWaitCount = ($timeWaitLines | Measure-Object).Count
"$time,$timeWaitCount" | Out-File -FilePath $csvFile -Append -Encoding UTF8
function Write-Log { param([string]$Message) $msg = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $Message" Write-Host $msg Add-Content $logFile $msg }
Write-Log "TIME_WAIT connections: $timeWaitCount"
if ($timeWaitCount -gt $Threshold) { Write-Log "-----------------------------" Write-Log "TIME_WAIT 超过阈值 ($Threshold),详细占用如下:"
$ports = $timeWaitLines | ForEach-Object { ($_ -split '\s+') | Where-Object { $_ -match "^\d+\.\d+\.\d+\.\d+:" } | ForEach-Object { ($_ -split ":")[-1] } }
$topPorts = $ports | Group-Object | Sort-Object Count -Descending | Select-Object -First 10 Write-Log "Top 10 本地端口 TIME_WAIT 占用:" foreach ($p in $topPorts) { Write-Log ("端口 {0,-8} : {1,5}" -f $p.Name, $p.Count) }
$remoteIPs = $timeWaitLines | ForEach-Object { ($_ -split '\s+') | Where-Object { $_ -match "^\d+\.\d+\.\d+\.\d+:\d+$" } | Select-Object -Last 1 } | ForEach-Object { ($_ -split ":")[0] }
$topIPs = $remoteIPs | Group-Object | Sort-Object Count -Descending | Select-Object -First 10 Write-Log "" Write-Log "Top 10 远端 IP TIME_WAIT 占用:" foreach ($ip in $topIPs) { Write-Log ("IP {0,-16} : {1,5}" -f $ip.Name, $ip.Count) }
$pids = $timeWaitLines | ForEach-Object { ($_ -split '\s+')[-1] } | Where-Object { $_ -ne "0" } | Group-Object | Sort-Object Count -Descending | Select-Object -First 10
if ($pids.Count -gt 0) { Write-Log "" Write-Log "Top 10 有效 PID TIME_WAIT 占用:" foreach ($p in $pids) { try { $procName = (Get-Process -Id $p.Name -ErrorAction SilentlyContinue).ProcessName } catch { $procName = "N/A" } Write-Log ("PID {0,-6} ({1,-15}) : {2,5}" -f $p.Name, $procName, $p.Count) } } else { Write-Log "" Write-Log "PID 信息:均为系统层 TIME_WAIT (PID=0),表示已断开的套接字等待回收。" }
Write-Log "-----------------------------`n" }
Start-Sleep -Seconds $IntervalSeconds }
|
脚本使用方法
1 2 3 4 5 6 7 8
| .\Monitor-TimeWait-Loop.ps1
.\Monitor-TimeWait-Loop.ps1 -IntervalSeconds 30 -Threshold 200
Start-Process powershell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File .\Monitor-TimeWait-Loop.ps1" -WindowStyle Hidden
|
输出示例
正常情况:
1 2
| 2025-10-20 10:00:00 - TIME_WAIT connections: 85 2025-10-20 10:01:00 - TIME_WAIT connections: 92
|
超过阈值时:
CSV 趋势分析
脚本会自动生成 CSV 文件记录 TIME_WAIT 连接数的变化趋势,可以使用 Excel 打开并插入折线图进行可视化分析:
扩展:Windows TCP 参数优化
如果通过监控发现 TIME_WAIT 连接数持续过高,可以考虑调整 Windows Server 的 TCP 参数来扩大可用端口范围和缩短等待时间。
查看当前配置
1 2 3 4 5
| # 查看最大用户端口数(默认可能不存在此键值) reg query "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v MaxUserPort
# 查看TIME_WAIT等待时长(默认可能不存在此键值) reg query "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v TcpTimedWaitDelay
|
Windows Server 2016 默认情况:
MaxUserPort:注册表中不存在此键值,系统默认约为 5000
TcpTimedWaitDelay:注册表中不存在此键值,系统默认为 240 秒(4 分钟)
优化配置
如果业务场景需要,可以通过以下命令扩展端口范围和缩短等待时间:
1 2 3 4 5
| # 设置最大用户端口为 65534(几乎使用全部动态端口范围) reg add "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v MaxUserPort /t REG_DWORD /d 65534 /f
# 设置TIME_WAIT等待时长为 30秒(默认240秒) reg add "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v TcpTimedWaitDelay /t REG_DWORD /d 30 /f
|
参数说明:
| 参数 |
说明 |
默认值 |
建议值 |
范围 |
| MaxUserPort |
最大用户端口数 |
~5000 |
65534 |
5000-65534 |
| TcpTimedWaitDelay |
TIME_WAIT 等待时长(秒) |
240 |
30-60 |
30-300 |
应用配置
修改注册表后,需要重启服务器才能生效:
1 2 3 4 5 6
| reg query "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v MaxUserPort reg query "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v TcpTimedWaitDelay
Restart-Computer -Force
|
验证配置
重启后验证配置是否生效:
1 2
| netsh int ipv4 show dynamicport tcp
|
注意事项
1. TIME_WAIT 的作用
- 不要设置过小:TcpTimedWaitDelay 不建议低于 30 秒
- 保证可靠性:过短可能导致网络可靠性问题
2. 端口范围
- 不要超过 65535:这是 TCP 端口的最大值
- 保留端口:1-1024 为系统保留端口
3. 应用场景
这些优化适用于:
- 高并发 Web 服务器
- API 网关
- 反向代理服务器
- 数据库连接池服务
不适用于:
- 普通办公电脑
- 低并发应用
- 对网络延迟敏感的实时应用
4. 监控建议
- 定期检查 TIME_WAIT 趋势
- 设置合理的告警阈值
- 保留历史数据用于分析
总结
通过 PowerShell 脚本持续监控 TIME_WAIT 连接,可以及时发现和分析端口使用情况。配合 CSV 趋势分析,能够清晰地了解系统的连接状态变化。
核心思路:
- 使用监控脚本实时跟踪 TIME_WAIT 连接数
- 分析连接来源(端口、IP、进程)定位问题
- 通过 CSV 数据生成趋势图辅助决策
- 必要时通过注册表参数优化扩展系统能力
监控建议:
- 根据业务特点设置合理的阈值
- 定期查看 CSV 趋势图发现异常模式
- 保留历史日志用于问题回溯
网络问题排查的关键在于数据:有了持续的监控数据,才能准确判断问题的严重程度和变化趋势。