新上线的站点起不来,日志里一句“Address already in use”;客户说访问不了你的服务,你却在服务器上找不到任何异常;安全同事发来一份漏洞清单,要求你确认这台机器到底开哪些端口对外。这三个场景的共同起点,都是同一件事:搞清楚 Windows Server 当前的端口监听状态。
Linux 上有 ss -lntp,Windows 上同样有完整的工具链。本文从最经典的 netstat -ano 讲到现代 PowerShell 的 Get-NetTCPConnection,再补充资源监视器、TCPView、PortQry 等图形与第三方工具,并给出 TCP 状态判读标准和“端口被占”的实战处理流程。
一、先建立概念:三种端口视图的区别
很多人一说“看端口”就敲 netstat -an,然后被几千行输出淹没。其实你需要先明确自己要回答的是哪个问题:
| 要回答的问题 | 该看什么 | 对应状态 / 命令要点 |
|---|---|---|
| 本机开了哪些服务端口对外 | 监听(Listening)套接字 | State = Listen;netstat -ano | findstr LISTENING |
| 现在谁在连我、连了多少 | 已建立(Established)连接 | State = Established,按远端 IP 聚合统计 |
| 某个端口为什么起不来 | 占用进程 PID / 排除端口范围 | -o 参数、netsh int ipv4 show excludedportrange |
| 连接是不是快耗尽了 | TIME_WAIT / CLOSE_WAIT 数量 | 按状态分组计数,判断阈值 |
TCP 状态速查,这几条最常用来判断故障:
- LISTENING:本地正在监听,等待连接。这是“服务有没有起来”的直接证据。
- ESTABLISHED:连接已建立,正在传输数据。
- TIME_WAIT:本端主动关闭后进入的等待状态,默认约 240 秒(2×MSL,注册表
TcpTimedWaitDelay可调,范围 30–300 秒,不同版本默认值略有差异)。大量堆积通常是短连接并发过高。 - CLOSE_WAIT:对端已关闭,本端还没关。这个状态持续大量出现基本是应用程序 Bug——代码里 socket 忘了 close,属于文件描述符/句柄泄漏,必须改程序。
- SYN_RECEIVED:半连接积压,短时间内大量出现要怀疑 SYN Flood 或后端处理不过来。
二、CMD 经典方案:netstat -ano
netstat 从 Windows NT 时代就在,兼容性最好,Server 2003 到 2025 都能跑,是排障第一反应命令。参数含义:-a 显示所有连接与监听端口,-n 以数字形式显示地址和端口(不加会做 DNS 反解,巨慢),-o 显示占用进程的 PID,-b 显示可执行程序名(需要管理员权限),-p tcp 只看 TCP。
REM 查看所有监听端口 + 对应 PID(最常用) netstat -ano | findstr LISTENING REM 精确查某个端口,注意冒号,避免匹配到 PID netstat -ano | findstr ":80 " netstat -ano | findstr ":443" REM 按状态筛选已建立连接 netstat -ano -p tcp | findstr ESTABLISHED REM 显示占用端口的可执行程序(必须管理员 CMD) netstat -anob REM 统计各状态连接数(需要按状态计数时用 find /c) netstat -ano -p tcp | find /c "ESTABLISHED" netstat -ano -p tcp | find /c "TIME_WAIT"
拿到 PID 之后反查进程:
REM 方式一:tasklist tasklist /fi "PID eq 4820" REM 方式二:强行结束(确认业务影响后再操作) taskkill /PID 4820 /F REM 方式三:wmic(老系统兼容) wmic process where processid=4820 get name,executablepath,commandline
一个实用技巧:把监听端口导出成文件做基线对比。服务器上线后跑一次 netstat -ano > C:\baseline_ports.txt,日后排查时用 fc 对比差异,能立刻发现“多出来”的可疑监听。
三、PowerShell 现代方案:Get-NetTCPConnection
Server 2012 / PowerShell 3.0 起提供了 Get-NetTCPConnection(模块 NetTCPIP),输出是对象而不是文本,可以直接筛选、排序、管道传递,比 netstat 强太多。强烈建议在 PowerShell 环境里优先用它。
# 列出所有监听端口(SNAT 绑定的除外),按端口排序
Get-NetTCPConnection -State Listen | Sort-Object LocalPort |
Select-Object LocalAddress, LocalPort, State, OwningProcess
# 一步到位:把 PID 换成进程名(最常用的一段)
Get-NetTCPConnection -State Listen |
Select-Object LocalAddress, LocalPort,
@{n='ProcessName';e={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName}},
@{n='Path';e={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).Path}} |
Sort-Object LocalPort | Format-Table -AutoSize
# 精确查某端口
Get-NetTCPConnection -LocalPort 3389 -ErrorAction SilentlyContinue
# 查到某个远端的所有连接(判断客户端是否连上来了)
Get-NetTCPConnection -RemoteAddress 203.0.113.25
# 按状态统计连接数,判断 TIME_WAIT 是否堆积
Get-NetTCPConnection | Group-Object State | Sort-Object Count -Descending |
Select-Object Count, Name
# UDP 端点用另一条 cmdlet(DHCP、DNS、SNMP 类服务常见) Get-NetUDPEndpoint | Select-Object LocalAddress, LocalPort, OwningProcess # 查看某进程占用的全部端口(先查 PID) $p = (Get-Process -Name nginx).Id Get-NetTCPConnection -OwningProcess $p -ErrorAction SilentlyContinue # 查看动态端口(临时端口)范围 Get-NetTCPSetting | Select-Object SettingName, DynamicPortRangeStartPort, DynamicPortRangeNumberOfPorts
一个高频需求:统计“哪个远端 IP 连接最多”,用于识别异常来源或 DDoS。一行管道就够:
Get-NetTCPConnection -State Established |
Where-Object { $_.RemoteAddress -notmatch '^(127\.|::1|fe80:)' } |
Group-Object RemoteAddress | Sort-Object Count -Descending |
Select-Object -First 10 Count, Name
四、端口被占用却查不到进程?检查排除端口范围
这是 Windows 特有、且极其容易让人抓狂的坑:明明 netstat 显示端口空闲,程序一 bind 就报 10013 / 10048 错误。原因通常是该端口落在了系统保留(excluded)端口范围内——Hyper-V、WSL、Docker、Windows NAT 会在启动时向系统申请一段端口保留,这些端口不在 netstat 里显示,但别人也用不了。
REM 查看 IPv4 TCP 排除端口范围(管理员权限) netsh int ipv4 show excludedportrange protocol=tcp REM 查看 IPv6 / UDP netsh int ipv6 show excludedportrange protocol=tcp netsh int ipv4 show excludedportrange protocol=udp REM 查看动态端口范围(Server 2008 起默认 49152-65535,共 16384 个) netsh int ipv4 show dynamicport tcp REM 查看具体保留项(Docker / Hyper-V 常见) netsh int ipv4 show excludedportrange protocol=tcp netsh int ipv4 show dynamicport udp
处理办法有三种:一是把应用端口改到保留范围之外;二是重启相关服务(如 Restart-Service hns 或停用 Hyper-V 后重启)让保留释放;三是用 netsh int ipv4 add excludedportrange 主动把自己要用的端口永久保留下来,避免被系统临时分配走。
另一个常见场景:80/443 被 HTTP.SYS(内核态 HTTP 驱动)占用,IIS、SQL Server Reporting Services、WinRM、Web 部署服务都会用到它。此时 netstat 里的 PID 往往是 4(System),需要用专门的命令看:
REM 查看 HTTP.SYS 的 URL 保留与监听队列 netsh http show servicestate view=requestq netsh http show urlacl netsh http show iplisten
五、图形化工具:不敲命令也能看清楚
并不是所有场合都适合命令行。给领导演示、给不懂技术的同事定位、或者在客户机上临时排查时,图形工具更直观。
| 工具 | 打开方式 | 适用场景 | 备注 |
|---|---|---|---|
| 资源监视器 | resmon.exe → “网络” 选项卡 → “侦听端口” | 系统自带,直观显示端口、进程、防火墙状态 | 无需联网安装,首选 |
| 任务管理器 | Ctrl+Shift+Esc → 性能 → 打开资源监视器 | 顺路查看时 | 功能较弱 |
| TCPView | Sysinternals 套件,官网下载 | 实时刷新、颜色标示状态变化、可右键杀进程 | 最推荐,绿色单文件 |
| PortQry / PortQryUI | Microsoft 下载中心 | 从客户端测试对端端口是否开放 | 输出 listening / filtered / not listening |
| Windows 防火墙日志 | %systemroot%\System32\LogFiles\Firewall\pfirewall.log | 确认是不是被防火墙丢包 | 需先启用日志记录 |
如果不想装任何东西,用 PowerShell 自带的 Test-NetConnection 从客户端探测对端端口,是判断是否被防火墙拦的最快手段:
# 从客户端探测服务端端口(替代 telnet,Windows 默认不装 telnet 客户端)
Test-NetConnection 192.168.10.20 -Port 8080
# 只要布尔结果,方便写脚本
(Test-NetConnection 192.168.10.20 -Port 8080 -WarningAction SilentlyContinue).TcpTestSucceeded
# 批量探测多个端口
3306,3389,8080,443 | ForEach-Object {
$r = Test-NetConnection 192.168.10.20 -Port $_ -WarningAction SilentlyContinue
[PSCustomObject]@{ Port=$_; Open=$r.TcpTestSucceeded }
}
命令输出解读:TcpTestSucceeded = True 说明链路和监听都正常;False 且 Ping 通,说明监听没起或被防火墙拦;Ping 不通则先看网络层。
六、标准操作清单:从“端口不通”到定位根因
- 确认服务监听:服务端执行
Get-NetTCPConnection -LocalPort <端口> -State Listen,无输出说明服务没起来或监听地址不对。 - 核对监听地址:LocalAddress 是
0.0.0.0表示所有网卡都听;若是127.0.0.1,则外部永远访问不到,需要改应用绑定配置。 - 反查进程:用 OwningProcess 拿 PID,再用
Get-Process -Id或tasklist /fi确认是哪个程序。 - 服务端本地自测:在服务本机执行
Test-NetConnection 127.0.0.1 -Port <端口>,排除应用自身问题。 - 跨机测试:从客户端测对端 IP 的端口,判断是网络/防火墙问题还是应用问题。
- 查防火墙:
Get-NetFirewallRule或直接看netsh advfirewall firewall show rule name=all中对应端口的入站规则。 - 查排除端口范围:bind 失败但端口空闲时,执行
netsh int ipv4 show excludedportrange protocol=tcp。 - 留基线:把正常状态下的监听列表导出存档,作为日后对比的依据。
小结与避坑建议
Windows Server 查看端口监听状态,日常记住两条命令就够用:CMD 下 netstat -ano | findstr LISTENING,PowerShell 下 Get-NetTCPConnection -State Listen 配合 Get-Process 把 PID 翻译成进程名。需要实时观察就用 TCPView,需要从外部探测就用 Test-NetConnection 或 PortQry。
几条实践建议:
- 永远加
-n。不加-n的 netstat 会对每个远端 IP 做反向 DNS 解析,在连接数多时可能卡住几十秒。 - 把端口基线做成日巡检项。一台服务器对外开的端口应该是固定的,多出一个监听就值得追查,这是发现后门和挖矿程序最朴素也最有效的手段。
- CLOSE_WAIT 堆积不要靠重启解决。重启只能临时清空,根因是应用程序没有关闭 socket,必须从代码或组件版本入手。
- TIME_WAIT 多不一定是问题。短连接服务(HTTP 轮询、API 调用)天生会产生大量 TIME_WAIT,只有当它接近动态端口上限(默认 16384 个)并伴随新建连接失败时才需要处理。
- 警惕 PID = 4 的占用。它代表 System 进程,真正的占用者往往是内核态的 HTTP.SYS 或其他驱动,要用
netsh http show servicestate才能看清。 - 生产环境不要凭 PID 直接 kill。先用
Get-Process -Id确认路径和命令行,误杀数据库或服务进程代价太大。
把这套方法整理成脚本放进运维工具箱,下次遇到“端口不通”,你就能在两分钟内给出确定答案,而不是靠重启碰运气。
企业QQ咨询




