ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Windows端口占用排查:netstat与tasklist定位进程

Windows端口占用排查:netstat与tasklist定位进程

上周帮朋友处理一台内网机器上的怪事:一个本地起的调试服务,启动时死活报端口被占用,机器上没装任何端口查看工具,远程桌面连上去只有一个黑黢黢的 cmd 窗口。很多人第一反应是去下载个图形化端口查看器,但在那种环境下,下载本身就是个问题。其实系统自带的netstat配合tasklist,两条命令就能把"哪个程序占着哪个 IP 的哪个端口"这件事说得明明白白。

这篇要聊的就是这个:仅靠 cmd 里自带的命令,抓取本机运行程序所占用的 IP 和端口。我会把netstat -ano输出里每一列的含义拆开讲,把 PID 翻译成程序名的两条路径都摆出来,再把我这些年踩过的编码坑、误匹配坑、权限坑一个个点掉,最后给一套可以直接抄走的批处理和一串 PowerShell 写法。适合谁看?服务端运维、做本地开发的、需要排查端口冲突的、以及手头只有一台受限机器但又必须搞清楚"谁在监听"的人。

需要先说明一点:这里的"抓取"指本机端口枚举——看自己这台机器上哪些进程开了哪些监听、有哪些连接。这是完全正当的自查动作,不涉及任何越界操作。

1. netstat -ano 那一行输出,到底该怎么断句

很多人敲完netstat -ano就被刷屏了,几十上百行密密麻麻,看两眼就放弃。其实它每一行都是高度规整的,只是你没建立"断句"的习惯。-a表示列出所有连接和监听端口,-n表示用数字形式显示地址和端口(不做反向域名解析,速度快很多),-o表示顺带把拥有该连接的进程 ID 打出来。三个参数少一个都不行:少了-a看不到监听态,少了-n会去做 DNS 反查导致卡顿几十秒,少了-o你就只有地址没有主人。

1.1 五个字段的边界:从 TCP 到 PID

一行典型的输出长这样:

TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 1044 TCP 192.168.1.27:52341 192.168.1.10:1433 ESTABLISHED 6120 UDP 0.0.0.0:500 *:* 1200

从左到右依次是:协议、本地地址、外部地址、状态、PID。行首那一个空格是固定存在的,这会影响后面用for /f解析时的 token 计数,先记着。

协议列只有 TCP 和 TCPv6、UDP 和 UDPv6 这几种。TCPv6 就是走 IPv6 栈的连接,地址会带上方括号,比如[::]:135。本地地址是"本机这一端"的 IP 加端口,格式永远是地址:端口,中间那个冒号是唯一的天然分隔符——但也正因为地址里本身可能含冒号(IPv6),所以不能简单用冒号去 split,这是后面写脚本时最容易翻车的地方。

外部地址是"对端"的 IP 加端口。监听态的时候它没有实际意义,TCP 会显示0.0.0.0:0,UDP 会显示*:*,都是占位符。只有连接建立了,这一列才会变成真实的远端地址,而这一列恰恰是判断"某个程序连到哪去了"的唯一线索。

PID 列是整行的落点。它是个纯数字,正常情况下在行尾,前面有若干空格。注意 PID 这一列在 UDP 行上也存在,但 UDP 行没有状态列,整个行的字段数量比 TCP 少一个,如果你写脚本按固定位置取 token,UDP 行一定会取错。

1.2 本地地址三态:0.0.0.0、127.0.0.1、具体网卡 IP 的含义差

本地地址这一列看着简单,其实藏着一个非常关键的语义区分,搞不清这个,端口冲突的排查方向就会完全跑偏。

0.0.0.0:8080表示这个程序在所有网卡上都监听 8080。也就是说同一局域网里其他机器能通过你这台机器的 IP 访问到它。这是最"开放"的一种绑定方式,也是绝大多数服务端程序的默认行为。

127.0.0.1:8080表示只在本机回环接口上监听。局域网里别人访问不到,只有本机上的其他进程能连。很多开发调试用的本地服务会这么绑,出于安全考虑是对的,但会带来一个副作用——你用另一台机器去测,永远连不上,然后开始怀疑防火墙,其实根本不是防火墙的事。

192.168.1.27:8080这种绑定到具体网卡 IP 的,就只在那张网卡上响应。机器换网络、IP 变了之后服务起不来,十有八九就是这个原因。

IPv6 那边还有[::]:8080,含义和0.0.0.0类似,是全 IPv6 地址监听。有意思的是在新一些的系统上,[::]的监听往往同时接管 IPv4 的连接(双栈),所以你会看到一个程序只写了[::]:8080,但 IPv4 也能连上。这一点在判断"是不是真的没人监听 8080"时很容易误判。

1.3 状态列不是装饰:LISTENING、ESTABLISHED、TIME_WAIT 各自说明什么

LISTENING是等待别人来连,这是"服务在跑"的标志。查端口占用,绝大多数时候你筛的就是这一种。

ESTABLISHED是连接已经建立、双方正在通信。这个状态下外部地址列才有真东西可看。如果你想搞清楚某个程序往外连了什么,筛这个状态。

TIME_WAIT是主动关闭连接的一方在等一小段时间,确保最后的确认包能被对端收到。这个状态很讨厌,因为它会让端口在关闭后还"占着"几十秒到两分钟。经常出现的情况是:你把服务停了,立刻重启,报端口被占用,netstat一查是TIME_WAIT。这时候不用慌,也不用杀进程——它的 PID 通常显示为 0,根本没有进程可杀,等它自己过期就行。

CLOSE_WAIT则是被动关闭的一方没把连接关掉,通常是程序代码里忘了 close。如果一台机器上堆了几百个CLOSE_WAIT,那基本可以断定是那个程序有连接泄漏,跟网络无关,跟端口冲突也无关。

SYN_SENT表示正在尝试连接对端但还没收到回应,大量出现说明目标不可达或者被中间设备拦了。这个状态在排查"为什么连不上某个服务"时很有用。

注意:UDP 是面向无连接的,所以 UDP 那几行压根没有状态列,永远是空白。你按状态去 grep 的时候,UDP 的行会被整体漏掉,这是个静默失败,不会有任何报错提示你。

2. 拿到 PID 只是起点,把它翻译成程序名还有两道坎

netstat -ano给你的是 PID,一个四位或五位的数字。人对数字是没有记忆的,你需要把它换成java.exe、node.exe或者svchost.exe这种名字,才知道该动谁。这一步有两条路,各有各的坑。

2.1 tasklist 过滤器语法:为什么你的 /FI 老是"没有匹配的任务"

tasklist是另一个系统自带命令,默认输出当前所有进程的映像名、PID、会话名、会话号、内存占用。要按 PID 精确定位,用过滤器:

tasklist /FI "PID eq 6120"

这里有几个必须记住的细节。第一,过滤器里的字段名和比较符必须是英文,PID、IMAGENAME、eq、ne、gt、lt这些,即使在中文系统上也不能写成中文,写成中文会直接报语法错误。第二,整个过滤表达式必须用双引号包起来,因为中间有空格。第三,eq后面的值不要加引号,加了反而匹配不上。

常用参数里,/NH是去掉表头,只出数据行;/FO CSV是把输出改成 CSV 格式,方便脚本解析;/SVC是额外显示每个进程承载了哪些服务,这个参数在查svchost.exe的时候是刚需。

最容易踩的一个坑是:tasklist /FI "PID eq 4"有时候会告诉你"信息: 没有运行的任务匹配指定标准"。PID 4 是 System 进程,它确实存在,但tasklist在某些会话环境下不显示它。遇到这种情况不要以为进程不存在,换成管理员权限的窗口重试,或者直接上 PowerShell 的Get-Process -Id 4。

2.2 wmic 取可执行路径,以及它在较新系统上的退场

知道进程名还不够,你往往还需要知道它的完整路径——判断这个程序是系统自带的还是用户自己装的,路径是最直接的证据。

wmic process where processid=6120 get name,executablepath,commandline /format:list

wmic的好处是输出结构化、能一次拿到命令行参数。几个书写要点:processid=后面不能有空格,where子句里字段名不区分大小写,/format:list会把结果竖着输出,避免宽表在窄窗口里被截断成一行乱码。

但现实是,wmic在较新版本的 Windows 上已经被标记为废弃,部分新出厂的系统里默认不再预装。如果你在脚本里重度依赖它,早晚会翻车。替代方案是 CIM:

Get-CimInstance Win32_Process -Filter "ProcessId=6120" | Select-Object Name, ExecutablePath, CommandLine

这条在 PowerShell 里跑,返回的是对象,字段名清晰,也不会被窗口宽度截断。功能上和wmic等价,但生命周期长得多。我的建议是:交互式临时查一下用wmic图个快,写进脚本里的一律用Get-CimInstance。

2.3 一段能直接用的批处理:端口、PID、进程名一次打出来

把前面两步拼起来,就是一段能直接落地的批处理。下面这个脚本会遍历所有监听态的端口,输出"本地地址 | PID | 进程名"三列:

@echo off setlocal enabledelayedexpansion echo 本地地址^|PID^|进程名 for /f "tokens=2,5" %%a in ('netstat -ano ^| findstr "LISTENING"') do ( set "addr=%%a" set "pid=%%b" set "pname=unknown" for /f "tokens=1 delims=," %%n in ('tasklist /FI "PID eq !pid!" /NH /FO CSV 2^>nul') do ( set "pname=%%~n" ) echo !addr!^|!pid!^|!pname! ) endlocal

几个关键点解释一下。tokens=2,5是因为监听行里第 2 个字段是本地地址、第 5 个字段是 PID(第 4 个是LISTENING)。%%~n那个波浪号是去掉 CSV 输出里自带的双引号,不加的话进程名会带着引号显示。2^>nul是把tasklist的错误输出吞掉——当 PID 对应的进程刚好在这几十毫秒里退出了,tasklist会报找不到,不吞掉的话屏幕上会刷一堆红字。setlocal enabledelayedexpansion配上!pid!这种写法,是因为在for循环体里动态读取变量必须用延迟展开,用%pid%会拿到循环开始前的旧值。

delims=,配tokens=1取的是 CSV 的第一列,也就是映像名。如果你的进程名里本身带逗号(极少见但存在),这个解析会错位,那时候只能改成按引号切分,属于极端情况,一般不折腾。

3. findstr 才是把噪音干掉的关键,但它对编码很敏感

netstat -ano一次输出几百行是常态,人眼扫是不现实的。findstr是 cmd 里唯一的原生文本过滤工具,用好它效率提升非常明显,但它对字符编码的敏感程度也会让人抓狂。

3.1 中文系统下 netstat 输出的编码真相

这里有个流传很广的误解:很多人以为中文系统下netstat的状态列会显示成"侦听""已建立",所以findstr "LISTENING"会失败。事实恰恰相反——状态值是协议层的英文常量,中文系统上依然是LISTENING、ESTABLISHED、TIME_WAIT,只有表头("活动连接""协议""本地地址"这些)被翻译了。

所以按状态过滤永远是安全的,这是最省事的切入点。真正会出问题的是你试图去过滤表头里的中文。findstr是按字节做子串匹配的,它不关心编码。如果你的批处理文件保存成了 UTF-8,而 cmd 窗口的当前代码页是 936(GBK),那么文件里的中文字节和命令输出里的中文字节不是同一串,匹配必然失败,而且不会有任何提示——你只会看到"什么都没查出来"。

我的做法很干脆:过滤条件里只写 ASCII 字符。数字、冒号、点、LISTENING、ESTABLISHED,这些在任何代码页下字节都是一致的,永远不会出问题。非要在批处理里输出中文提示,就把文件另存为 ANSI 编码,别用 UTF-8。

提示:不要在批处理开头随手加chcp 65001。它确实会把控制台切成 UTF-8,但netstat这类老程序的输出编码不一定跟着变,结果是中文字符串一半正常一半乱码,比不加还乱。除非你有明确的编码统一需求,否则保持默认就好。

3.2 三个常用过滤维度的写法与误匹配边界

按端口、按 PID、按状态,是三种最高频的过滤方式,每一种的误匹配边界都不一样。

按端口查:

netstat -ano | findstr ":8080"

这是最直觉的写法,但它有个明显缺陷:findstr做的是子串匹配,:8080会同时命中:18080、:80800、:80801,甚至远端地址里带 8080 的连接也会被捞出来。要收紧一点,加个尾随空格:

netstat -ano | findstr ":8080 "

因为端口后面紧接着要么是空格(分隔到下一列),要么是行尾。加了空格能排除:80800这种超长端口,但仍然排除不掉:18080——它的子串里确实不含:8080,等等,其实:18080里包含的是:18080,不含:8080,所以加空格这条已经能挡住了。真正挡不住的是远端端口恰好是 8080 的连接行,比如你本机连了对面服务器的 8080,那一行的远端地址是x.x.x.x:8080,也会被匹配出来。要彻底区分本地和远端,findstr无能为力,得用第 5 节的 PowerShell 方案按列筛。

按 PID 查:

netstat -ano | findstr " 6120"

前面加空格是为了避免 PID 数字作为子串误中其他列。但即使这样,如果某个 IP 地址段里恰好含6120这种片段,理论上还是可能误中。实际概率很低,可以接受。

按状态查:

netstat -ano | findstr "ESTABLISHED"

这个最干净,不会有误匹配,因为状态值是固定枚举。想同时看监听和已建立,写成findstr "LISTENING ESTABLISHED"——findstr多个空格分隔的词之间是"或"的关系,命中任意一个就输出。

还有一个组合写法值得记住:findstr /V是反向过滤,netstat -ano | findstr /V "TIME_WAIT"可以把一堆等待中的连接直接剔掉,屏幕立刻清爽。

3.3 把高频查询固化成菜单式 .bat

每次手敲这一长串,敲几次就烦了。我的习惯是在一个固定目录放一个port.bat,双击之后给个简单菜单,输入端口号就直接出结果:

@echo off setlocal enabledelayedexpansion set /p p=请输入端口号: echo. echo ==== 端口 %p% 的占用情况 ==== netstat -ano | findstr ":%p% " echo. for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":%p% " ^| findstr "LISTENING"') do ( echo ---- PID %%a 对应的进程 ---- tasklist /FI "PID eq %%a" /FO LIST wmic process where processid=%%a get executablepath /format:list 2^>nul ) echo. pause endlocal

这个脚本的价值在于把"查端口 → 拿 PID → 查进程 → 拿路径"四步串成一次输入。真实排查现场,你需要的是连续动作而不是一个个命令去拼。把它放在桌面或者放到 PATH 里,长期收益很高。

有一个小细节:findstr "LISTENING"那一段是为了避免把TIME_WAIT行里 PID 为 0 的情况也送去tasklist,那样会得到一堆没用的提示。但如果这个端口确实处于TIME_WAIT,你就什么都查不到,会误以为端口没被占。所以实际用的时候,我会先把完整结果打出来,再单独处理监听态。

4. 端口被占用:从报错到锁定进程的完整排查链路

前面讲的都是零件,这一节把它们装成一台机器。端口冲突是最常见的触发场景,完整链路走一遍,以后遇到任何"端口起不来"都能照着推。

4.1 四步定位法,每一步看什么

第一步,从报错信息里把端口号抠出来。程序报"地址已被使用""bind failed""10048"这类,括号里或者日志上一行通常会带上具体端口。拿到端口,后面才有方向。

第二步,列出这个端口的全部相关行:

netstat -ano | findstr ":8080 "

这时候你会看到几种可能。如果只有一行LISTENING,说明确实有程序占着,直接进第三步。如果只有TIME_WAIT,那就是缓释期,等一两分钟或者换端口。如果一行都没有,说明端口其实没被占,报错另有原因——可能是程序压根没启动成功,也可能是它绑的是另一个端口而你记错了,还可能是绑定到了某个不存在的网卡 IP 上。

第三步,拿 PID 换进程名:

tasklist /FI "PID eq 6120" /FO LIST

/FO LIST是竖排输出,一行一个字段,比默认的横排表格好读得多,尤其在窄窗口里不会折行。

第四步,拿完整路径,确认这个进程到底是谁:

wmic process where processid=6120 get name,executablepath /format:list

路径在系统目录下(比如C:\Windows\System32之类)的,基本是系统服务;路径在用户目录、临时目录、或者某个奇怪命名的文件夹下的,就要多留个心眼。这一步是判断"该不该动它"的关键依据,不要跳过。

4.2 监听地址写错导致的"假冲突"

有一种情况特别迷惑人:netstat里明明看到 8080 被占了,但你访问http://127.0.0.1:8080却连不上。

原因通常是占用方绑的是具体网卡 IP,比如192.168.1.27:8080,而你去连的是回环地址。这两个在协议栈里是不同的绑定,互相不通。反过来也一样,绑在127.0.0.1上的服务,你用本机的局域网 IP 去访问同样连不上。

还有一种更隐蔽的:绑定的是0.0.0.0:8080,理论上回环也能通,但如果同时存在另一个程序绑定127.0.0.1:8080,在部分系统版本上这两个会互相冲突,谁先起谁赢,后起的报错。这时候netstat里能看到两行本地地址不同的记录,不了解这个机制的人会以为是幽灵进程。

排查时的动作是:盯着本地地址那一列看,而不是只看端口号。同一个端口号出现在不同的绑定地址上,是完全不同的两件事。

4.3 PID 4、PID 0 与 svchost:三个特殊的归属问题

有三个 PID 值得单独拎出来说,它们经常让人卡住。

PID 4是 System 进程。你在 80、443 端口上看到它,几乎可以确定是系统里的 HTTP 服务组件在占着,可能是某个 Web 服务、也可能是远程管理组件。这种情况你没法用taskkill去杀(会提示拒绝访问),正确做法是去服务管理器里找到对应的服务停掉,或者改用别的端口。硬来的话,只能重启机器。

PID 0出现在TIME_WAIT行上,表示这个连接已经不属于任何活着的进程了,纯粹是协议栈在等超时。没有进程可杀,也没有进程可查。看到 PID 0 不要浪费时间去tasklist,直接等。

svchost.exe的问题在于一个映像名对应几十个 PID,每个 PID 承载不同的系统服务。光看进程名完全无法判断它到底在干什么。这时候必须加/SVC:

tasklist /SVC /FI "PID eq 1848"

输出里会列出这个 PID 承载的所有服务名。拿到服务名之后,再去服务管理器里查它的显示名称和描述,才能知道该不该动。绝对不要直接对 svchost 下手,杀掉一个承载关键服务的实例,轻则网络断开,重则系统需要重启。这是我在早期踩过的坑,代价是一次远程会话直接失联。

5. PowerShell 原生查询:结构化输出的降维打击

前面所有内容都建立在文本解析之上,而文本解析天生脆弱——列宽一变、语言一换、多个空格少个空格,脚本就崩了。如果环境允许开 PowerShell,我强烈建议直接上结构化的查询方式。

5.1 Get-NetTCPConnection 相比 netstat 多给了什么

Get-NetTCPConnection -State Listen | Select-Object -First 10

返回的是一个对象集合,每一行都是独立的记录,字段名明确:LocalAddress、LocalPort、RemoteAddress、RemotePort、State、OwningProcess、CreationTime。你不需要再去数第几个 token,也不用担心 IPv6 地址里的冒号把解析搞乱。

相比netstat,它多出的最有价值的一个字段是CreationTime。用它可以判断"这个连接是什么时候建立的"。排查可疑外连时,一个刚建立三秒的连接和一个建立了三天的连接,意义完全不同。

另一个优势是它天然支持按列筛选,而不是全文子串匹配:

Get-NetTCPConnection -LocalPort 8080 -ErrorAction SilentlyContinue

这里-LocalPort是精确匹配本地端口,绝不会误中:18080,也绝不会把远端端口为 8080 的连接捞进来。这就是结构化查询和文本 grep 的本质区别。

5.2 一行命令拼出"端口 进程名 远端地址"

把端口和进程名拼到一起,用计算属性就够:

Get-NetTCPConnection | Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, State, OwningProcess, @{Name='Process'; Expression={ (Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName }} | Sort-Object LocalPort | Format-Table -AutoSize

@{Name=...; Expression={...}}这个语法是 PowerShell 的计算属性,让你在输出的时候临时算一个新列出来。-ErrorAction SilentlyContinue是必须加的,因为 PID 4 和 PID 0 调Get-Process会抛异常,不加会刷一屏红字。

Sort-Object LocalPort让它按端口排好,读起来舒服很多。Format-Table -AutoSize会自动调整列宽,避免被窗口截断。

如果想更狠一点,直接按进程分组看谁开得最多:

Get-NetTCPConnection -State Listen | Group-Object OwningProcess | Sort-Object Count -Descending | Select-Object -First 10 Count, @{n='Process';e={(Get-Process -Id $_.Name -ErrorAction SilentlyContinue).ProcessName}}, @{n='Pid';e={$_.Name}}

这个能看到"哪个程序监听了一堆端口",对于排查异常行为很有参考价值。

5.3 老系统上的降级写法

不是所有环境都有NetTCPIP模块,比较老的系统上Get-NetTCPConnection可能直接报"无法识别"。这时候有两条降级路。

一条是继续用netstat,但用 PowerShell 去解析它的输出,比在 cmd 里写for /f舒服得多:

netstat -ano | Select-String 'LISTENING' | ForEach-Object { $f = ($_ -split '\s+') | Where-Object { $_ } [PSCustomObject]@{ LocalAddress = $f[1] Pid = $f[4] } }

-split '\s+'按任意空白切分,Where-Object { $_ }把切出来的空字符串清掉——因为netstat行首有空格,直接切会产生空元素,下标会整体偏移一位。这个"清空元素"的动作是这段代码能正确工作的关键,少了它取到的全是错位的值。

另一条是用 CIM 查连接表,但那个查询比较慢,日常用不上,除非你要在脚本里做长期采集合规审计。

6. 从查得到到查得准:几条长期踩出来的经验

命令本身不难,难的是结果的可信度。同样一条netstat,在不同权限、不同会话下跑出来的东西是不一样的,这一点官方文档里不会强调。

6.1 权限差异造成的"看不见"

普通权限和管理员权限下,tasklist能看到的东西不一样。跨会话的进程、某些系统进程的详细信息,在非管理员窗口里是查不到的。最典型的症状是:netstat明明显示某个 PID 在监听,但tasklist过滤这个 PID 却告诉你没有匹配项。

netstat -b这个参数更极端,它直接在输出里附带可执行文件名,看着很美好,但有两个致命问题:一是必须管理员权限,否则全部显示"请求的操作需要提升";二是极慢,因为它要给每个连接枚举模块,端口多的机器上跑一次能卡十几秒。

所以我的实际选择是:始终用管理员身份开一个 cmd 或 PowerShell 窗口,然后走netstat -ano加tasklist的组合。-b只在万不得已的时候用一次,不写进脚本。

注意:部分受限环境里,即使提升也查不到某些由安全软件托管的进程路径。这时候不要死磕,换个方向——看它的网络行为、看它监听的端口范围,比看路径更有信息量。

6.2 输出留存与时间戳

排查是动态过程。你在十点看到某个连接存在,十点零五可能就没了。光靠翻屏往回找是不现实的。

我习惯把每次抓取都落成文件:

netstat -ano > ports_%date:~0,4%%date:~5,2%%date:~8,2%.txt

>是覆盖,>>是追加。做对照观察的时候用追加,隔一段时间跑一次,最后能看出变化趋势。文件名里带日期,避免覆盖。要注意%date%这个变量在不同区域设置下的格式不一样,上面这种切片写法只在yyyy/MM/dd格式下成立,稳妥一点的做法是在脚本里显式拼:

set "ts=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%" set "ts=%ts: =0%" netstat -ano > "ports_%ts%.txt"

那句%ts: =0%是把小时数为个位数时产生的空格替换成 0,否则文件名里会带空格,后续处理很麻烦。这是个很小的细节,但它决定了你的文件能不能被其他脚本批量处理。

在for /f循环内部做重定向,>必须写成^>,因为>在括号块里会被当作语法符号优先解析。这条也是踩过才知道的。

6.3 判断一条外连是否正常的几个观察点

抓到数据之后,怎么判断哪些是正常的、哪些值得关注?我一般看四个点。

第一看远端地址的网段。内网段之间的连接,通常是内部服务调用。出现一个完全陌生的公网地址,且对应进程是你没见过的,那就值得点开看看。看完进程路径和命令行参数,基本就能定性。

第二看远端端口。443 和 80 是常规的 Web 通信,3389 是远程桌面,1433 和 3306 是数据库。如果某个进程在往外连一个高位随机端口,比如 54321,那可能是自定义协议或者对等通信,也可能是别的东西,需要结合进程再判断。

第三看连接数量。单个进程维持几十个连接是正常的(数据库连接池、消息队列客户端都会这样)。但如果一个你完全不认识的进程同时开着几百个外部连接,那就很不正常了。

第四看时间。凌晨三点,一台没人用的办公机上某个进程突然发起大量外连,这个信号本身就值得看一眼。Get-NetTCPConnection的CreationTime字段在这里就体现出价值了。

需要说明的是,这些观察点只能帮你缩小范围,定性还得靠对业务本身的了解。我在实际操作中的体会是:先把正常基线摸清楚,异常才看得出来。平时没事的时候跑一遍,把结果存下来,知道这台机器"正常长什么样",等到出问题的时候才有对照物。临时抱佛脚去查,往往越查越乱。

另外还有一个我觉得特别实用的小习惯:把Get-NetTCPConnection -State Listen | Select LocalPort, OwningProcess这条命令的输出做成一个每日快照,存成带日期的文件。新装了什么软件、哪个服务偷偷开了监听端口,对比两天就能看出来。这比事后去翻安装记录快得多,而且不需要任何额外工具,一条命令的事。

真要说这套东西的核心,其实就是把netstat -ano这一行输出彻底读懂,再老老实实把 PID 翻译成"名字 + 路径 + 归属服务"三层信息。命令是死的,判断是活的,多查几次,这台机器上谁在干什么,心里就有数了。

返回列表