端口管理完全指南:PowerShell / Linux Bash / Git Bash
适用场景:开发时查端口占用、杀僵尸进程、调试服务连接、配置防火墙规则。
适用版本:Windows 10/11、PowerShell 7+、现代 Linux(iproute2)、Git Bash。
最后核对:2026-07-30
30 秒快速路径
Get-NetTCPConnection -LocalPort 4321 -State Listen
Get-Process -Id (Get-NetTCPConnection -LocalPort 4321 -State Listen).OwningProcess
Test-NetConnection localhost -Port 4321
目录
核心概念
端口是什么
端口是传输层协议用来区分网络通信端点的数字编号(0-65535)。通常情况下,同一协议、同一本地地址和同一端口组成的监听端点只能被一个 socket 占用;SO_REUSEPORT、父子进程共享 socket、不同本地地址或 IPv4/IPv6 分别监听等情况例外。
关键术语
| 术语 | 含义 |
|---|---|
| LISTEN | 进程正在监听该端口,等待连接 |
| ESTABLISHED | 已有活跃连接 |
| TIME_WAIT | 主动关闭方暂时保留已关闭连接的状态,防止延迟报文影响新连接;不等于整个端口被锁定 |
| PID | Process ID,进程唯一标识符 |
| 0.0.0.0 | 监听所有网卡接口 |
| 127.0.0.1 | 仅监听本机回环(localhost) |
| ::1 | IPv6 的 localhost |
常用端口号
| 端口 | 服务 |
|---|---|
| 80 | HTTP |
| 443 | HTTPS |
| 3000 | React/Next.js 开发服务器 |
| 4321 | Astro 开发服务器 |
| 5173 | Vite 开发服务器 |
| 8080 | 常见代理/开发端口 |
| 3306 | MySQL |
| 5432 | PostgreSQL |
| 6379 | Redis |
思维模型
所有端口操作的核心链路:
端口号 → PID(进程ID)→ 进程名/命令行
反向也成立:
进程名 → PID → 它占了哪些端口
PowerShell (Windows 原生)
查看端口占用
# ═══ 基础查询 ═══
# 查看所有正在监听的 TCP 端口
Get-NetTCPConnection -State Listen
# 查看特定端口的监听者
Get-NetTCPConnection -LocalPort 4321 -State Listen
# 查看特定端口 + 进程名(最实用的一行)
Get-NetTCPConnection -LocalPort 4321 -State Listen |
Select-Object LocalPort, OwningProcess,
@{N='Process'; E={(Get-Process -Id $_.OwningProcess).ProcessName}}
# ═══ 格式化输出 ═══
# 所有监听端口,按端口号排序,表格显示
Get-NetTCPConnection -State Listen |
Select-Object LocalPort, OwningProcess,
@{N='Name'; E={(Get-Process -Id $_.OwningProcess).ProcessName}} |
Sort-Object LocalPort |
Format-Table -AutoSize
# 只看 node 进程占了哪些端口
Get-NetTCPConnection -State Listen |
Where-Object { (Get-Process -Id $_.OwningProcess).ProcessName -eq 'node' }
# ═══ UDP 端口(较少用)═══
Get-NetUDPEndpoint | Select-Object LocalPort, OwningProcess
杀掉占端口的进程
# 只选择监听该端口的进程,并去重
$listenerPids = Get-NetTCPConnection -LocalPort 4321 -State Listen -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty OwningProcess -Unique
if ($listenerPids) {
# 先确认进程。优先在原终端按 Ctrl+C,让服务自行清理退出
Get-Process -Id $listenerPids
# 确认后终止
$listenerPids | ForEach-Object { Stop-Process -Id $_ }
# 只有普通终止失败且确认目标无误时,才以管理员身份强制终止
# $listenerPids | ForEach-Object { Stop-Process -Id $_ -Force }
} else {
Write-Host "TCP 4321 当前没有监听者"
}
通过 PID 查进程详情
# 基本信息
Get-Process -Id 18992
# 查看完整命令行(能看到进程跑的是什么脚本/文件)
Get-CimInstance Win32_Process -Filter "ProcessId=18992" |
Select-Object ProcessId, Name, CommandLine
# 查看进程的父进程(谁启动了它)
Get-CimInstance Win32_Process -Filter "ProcessId=18992" |
Select-Object ProcessId, ParentProcessId, Name
测试端口连通性
# 完整测试(输出详细信息)
Test-NetConnection -ComputerName localhost -Port 4321
# 只看结果 True/False
(Test-NetConnection localhost -Port 4321).TcpTestSucceeded
# 测试远程主机
Test-NetConnection -ComputerName google.com -Port 443
# 批量测试多个端口
@(80, 443, 3000, 4321, 8080) | ForEach-Object {
[PSCustomObject]@{
Port = $_
Open = (Test-NetConnection localhost -Port $_ -WarningAction SilentlyContinue).TcpTestSucceeded
}
} | Format-Table
netstat 传统用法(PowerShell 里也能跑)
# 所有监听端口
netstat -ano | findstr LISTENING
# 特定端口
netstat -ano | findstr :4321
# 输出格式解读:
# 协议 本地地址 远程地址 状态 PID
# TCP 0.0.0.0:4321 0.0.0.0:0 LISTENING 18992
防火墙管理(需要管理员权限)
# 查看所有启用的入站规则
Get-NetFirewallRule -Direction Inbound -Enabled True |
Get-NetFirewallPortFilter |
Where-Object LocalPort -ne $null |
Select-Object @{N='Rule';E={(Get-NetFirewallRule -AssociatedNetFirewallPortFilter $_).DisplayName}},
LocalPort, Protocol |
Sort-Object LocalPort
# 查看允许 TCP 4321 入站的已启用规则
Get-NetFirewallRule -Direction Inbound -Enabled True -Action Allow |
Where-Object {
$portFilter = $_ | Get-NetFirewallPortFilter
$portFilter.Protocol -eq 'TCP' -and $portFilter.LocalPort -contains '4321'
} |
Select-Object DisplayName, Enabled, Direction, Action, Profile
# 开放端口
New-NetFirewallRule -DisplayName "Allow Port 4321" `
-Direction Inbound -LocalPort 4321 -Protocol TCP -Action Allow
# 删除规则
Remove-NetFirewallRule -DisplayName "Allow Port 4321"
# 临时禁用规则(不删除)
Disable-NetFirewallRule -DisplayName "Allow Port 4321"
Linux Bash / WSL
查看端口占用
# ═══ ss 命令(推荐,速度快)═══
# 所有监听的 TCP 端口
ss -tlnp
# 参数含义:t=TCP l=Listen n=数字显示(不解析域名) p=显示进程
# 查特定 TCP 监听端口(避免 grep 把 14321 也匹配进来)
ss -ltnp 'sport = :4321'
# 输出示例:LISTEN 0 511 *:4321 *:* users:(("node",pid=1234,fd=18))
# 包含 UDP
ss -tulnp
# ═══ lsof 命令(更直观)═══
# 查特定 TCP 监听端口(查看其他用户的进程通常需要 sudo)
sudo lsof -nP -iTCP:4321 -sTCP:LISTEN
# 输出示例:
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# node 1234 user 18u IPv4 12345 0t0 TCP *:4321 (LISTEN)
# 只输出监听者 PID(方便管道给 kill)
sudo lsof -t -iTCP:4321 -sTCP:LISTEN
# 查某个进程打开的所有网络连接
lsof -i -a -p 1234
# ═══ netstat(老工具,有些系统已移除)═══
netstat -tlnp
# 如果提示 command not found,装一下:apt install net-tools
# ═══ fuser(另一种方式)═══
# 查谁占了端口
fuser 4321/tcp
# 输出:4321/tcp: 1234
# 直接杀会影响所有匹配该端口的进程,先确认输出再使用
fuser -k 4321/tcp
杀掉占端口的进程
# 标准做法:只查 TCP 监听者,确认后再终止
sudo lsof -nP -iTCP:4321 -sTCP:LISTEN
listener_pids=$(sudo lsof -t -iTCP:4321 -sTCP:LISTEN)
[ -n "$listener_pids" ] && ps -fp $listener_pids
[ -n "$listener_pids" ] && kill $listener_pids # SIGTERM
# 只有进程拒绝正常退出时才使用 SIGKILL
[ -n "$listener_pids" ] && kill -9 $listener_pids
# 知道 PID 直接杀
kill 1234 # SIGTERM(进程可以优雅退出)
kill -9 1234 # SIGKILL(立即终止,不给机会清理)
kill -15 1234 # 同 kill,SIGTERM 的数字形式
# 按进程名杀(杀所有同名进程)
killall node # 杀所有 node 进程
pkill -f "astro dev" # 按命令行模式匹配杀
信号(Signal)说明
# Linux 的 kill 不是"杀",是"发信号"
kill -l # 列出所有信号
# 常用信号:
# SIGTERM (15) - 温和终止,进程可以捕获并优雅退出
# SIGKILL (9) - 强制终止,进程无法捕获,立即死
# SIGHUP (1) - 挂起,有些服务用来重新加载配置
# SIGINT (2) - 中断,等同于 Ctrl+C
通过 PID 查进程详情
# 查看进程基本信息
ps -p 1234 -o pid,ppid,user,comm,args
# 查看完整命令行
cat /proc/1234/cmdline | tr '\0' ' '
# 查看进程的环境变量(可能包含密码、Token 等敏感信息,不要粘贴到日志或聊天)
cat /proc/1234/environ | tr '\0' '\n'
# 查看进程的工作目录
ls -l /proc/1234/cwd
# 查看进程树(谁启动了谁)
pstree -p 1234
测试端口连通性
# nc (netcat) — 最通用
nc -zv localhost 4321
# -z 扫描模式(不发数据) -v 详细输出
# 成功:Connection to localhost 4321 port [tcp/*] succeeded!
# 失败:nc: connect to localhost port 4321 (tcp) failed: Connection refused
# 用 curl 测试 HTTP 服务
curl -s -o /dev/null -w "%{http_code}" http://localhost:4321
# 任何 HTTP 状态码都表示 HTTP 服务作出了响应;是否健康要按应用约定判断
# 用 bash 内置(不依赖外部工具)
(echo > /dev/tcp/localhost/4321) 2>/dev/null && echo "开放" || echo "关闭"
# 批量扫描端口
for port in 80 443 3000 4321 8080; do
(echo > /dev/tcp/localhost/$port) 2>/dev/null && echo "$port: 开放" || echo "$port: 关闭"
done
防火墙管理
# ═══ ufw(Ubuntu 简化防火墙)═══
sudo ufw status # 查看状态
sudo ufw allow 4321/tcp # 开放端口
sudo ufw deny 4321/tcp # 拒绝端口
sudo ufw delete allow 4321/tcp # 删除规则
# ═══ iptables(部分系统使用;现代发行版可能采用 nftables 或兼容层)═══
# 注意规则顺序和持久化方式因发行版而异
sudo iptables -L -n # 列出所有规则
sudo iptables -A INPUT -p tcp --dport 4321 -j ACCEPT # 开放
sudo iptables -A INPUT -p tcp --dport 4321 -j DROP # 拒绝
sudo iptables -D INPUT -p tcp --dport 4321 -j ACCEPT # 删除
# ═══ firewalld(CentOS/Fedora)═══
sudo firewall-cmd --list-ports
sudo firewall-cmd --add-port=4321/tcp --permanent
sudo firewall-cmd --reload
Git Bash (MINGW64)
Git Bash 提供 Bash 和部分 Unix 工具,但通常没有 Linux 的 ss、lsof。kill 是 Bash 内建命令,主要适用于 MSYS 进程;管理任意 Windows 原生进程时通常仍使用 netstat、tasklist 和 taskkill。
查看端口占用
# 查特定 TCP 监听端口(精确匹配本地端口)
netstat -ano | awk '$1 == "TCP" && $2 ~ /:4321$/ && $4 == "LISTENING"'
# 输出: TCP 0.0.0.0:4321 0.0.0.0:0 LISTENING 18992
# 所有监听端口
netstat -ano | grep LISTEN
# 提取并去重 PID
netstat -ano | awk '$1 == "TCP" && $2 ~ /:4321$/ && $4 == "LISTENING" {print $5}' | sort -u
# 输出: 18992
杀掉占端口的进程
# 知道 PID 后先确认,再终止(注意双斜杠)
tasklist //FI "PID eq 18992"
taskkill //PID 18992
# 只有普通终止失败且目标无误时才强制终止
taskkill //F //PID 18992
# 按进程名杀
taskkill //F //IM node.exe
# 组合:精确查到监听者 PID,先显示,确认后再逐个终止
target_pids=$(netstat -ano | awk '$1 == "TCP" && $2 ~ /:4321$/ && $4 == "LISTENING" {print $5}' | sort -u)
for target_pid in $target_pids; do tasklist //FI "PID eq $target_pid"; done
for target_pid in $target_pids; do taskkill //PID "$target_pid"; done
查进程信息
# 查所有进程
tasklist
# 过滤进程名
tasklist | grep node
# 过滤 PID
tasklist | grep 18992
测试端口
# curl 测试(Git Bash 自带 curl)
curl -s -o /dev/null -w "%{http_code}" http://localhost:4321
# 查看完整 HTTP 交互;HTTP 错误状态也说明服务作出了响应
curl -v http://localhost:4321
Git Bash 的坑
# 1. 参数前缀用 // 而不是 /
taskkill //F //PID 1234 # 正确
taskkill /F /PID 1234 # 错误!Git Bash 会把 / 当路径
# 2. 通常没有 Linux 的 ss、lsof
# kill 是 Bash 内建命令,但不能当作管理所有 Windows 原生进程的通用工具
# 3. 路径分隔符混乱
# Windows 路径:C:\Users\yourname
# Git Bash 路径:/c/Users/yourname
# Windows 原生程序通常接受 Windows 路径,Unix 工具通常使用 /c/...;
# MSYS 的自动路径转换并非对所有参数都可靠
三平台速查对照表
日常操作
| 操作 | PowerShell | Linux Bash | Git Bash |
|---|---|---|---|
| 所有监听端口 | Get-NetTCPConnection -State Listen |
ss -tlnp |
netstat -ano | grep LISTEN |
| 查谁监听 4321 | Get-NetTCPConnection -LocalPort 4321 -State Listen |
sudo lsof -nP -iTCP:4321 -sTCP:LISTEN |
netstat -ano | awk '$1=="TCP" && $2~/:4321$/ && $4=="LISTENING"' |
| 只拿 PID | (... | Select-Object -ExpandProperty OwningProcess -Unique) |
sudo lsof -t -iTCP:4321 -sTCP:LISTEN |
上一条命令后加 {print $5} 并 sort -u |
| 终止已确认的 PID | Stop-Process -Id 18992 |
kill 1234 |
taskkill //PID 18992 |
| 强制终止(最后手段) | Stop-Process -Id 18992 -Force |
kill -9 1234 |
taskkill //F //PID 18992 |
| 测试 TCP 端口 | Test-NetConnection localhost -Port 4321 |
nc -zv localhost 4321 |
powershell.exe -NoProfile -Command "Test-NetConnection localhost -Port 4321" |
| PID → 进程名 | (Get-Process -Id 18992).ProcessName |
ps -p 1234 -o comm= |
tasklist //FI "PID eq 18992" |
| 按名字杀进程 | Stop-Process -Name node |
killall node |
taskkill //IM node.exe |
进阶操作
| 操作 | PowerShell | Linux Bash | Git Bash |
|---|---|---|---|
| 查进程命令行 | Get-CimInstance Win32_Process -Filter "ProcessId=18992" | Select CommandLine |
cat /proc/1234/cmdline | tr '\0' ' ' |
powershell.exe -NoProfile -Command "Get-CimInstance Win32_Process -Filter 'ProcessId=18992' | Select-Object -ExpandProperty CommandLine" |
| 某程序占的所有端口 | Get-NetTCPConnection -OwningProcess (Get-Process -Name node).Id |
ss -tlnp | grep node |
netstat -ano | grep $(tasklist | grep node | awk '{print $2}') |
| 开放防火墙端口 | New-NetFirewallRule ... |
sudo ufw allow 4321/tcp |
N/A (用 PowerShell) |
| 批量测端口 | ForEach-Object 循环 | for 循环 + /dev/tcp | for 循环 + curl |
进阶用法
监控端口变化(实时观察)
# PowerShell:每 2 秒刷新一次监听端口列表
while ($true) {
Clear-Host
Get-NetTCPConnection -State Listen |
Select LocalPort, OwningProcess, @{N='Name';E={(Get-Process -Id $_.OwningProcess).Name}} |
Sort LocalPort | Format-Table -AutoSize
Start-Sleep 2
}
# Linux:用 watch 命令
watch -n 2 'ss -tlnp'
# 或者持续监控某个端口是否出现
while true; do ss -ltn 'sport = :4321' | grep -q LISTEN && echo "UP" || echo "DOWN"; sleep 1; done
等待端口就绪(脚本里常用)
# PowerShell:等待服务启动
function Wait-Port {
param([int]$Port, [int]$Timeout = 30)
$start = Get-Date
while (((Get-Date) - $start).TotalSeconds -lt $Timeout) {
if ((Test-NetConnection localhost -Port $Port -WarningAction SilentlyContinue).TcpTestSucceeded) {
Write-Host "端口 $Port 已就绪"
return $true
}
Start-Sleep 1
}
Write-Host "超时:端口 $Port 未就绪"
return $false
}
Wait-Port -Port 4321 -Timeout 15
# Linux:等待端口就绪
wait_port() {
local port=$1 timeout=${2:-30} elapsed=0
while [ $elapsed -lt $timeout ]; do
(echo > /dev/tcp/localhost/$port) 2>/dev/null && echo "端口 $port 已就绪" && return 0
sleep 1
elapsed=$((elapsed + 1))
done
echo "超时:端口 $port 未就绪" && return 1
}
wait_port 4321 15
查看端口对应的完整连接信息
# PowerShell:查看某端口的所有连接状态
Get-NetTCPConnection -LocalPort 4321 |
Select-Object State, LocalAddress, LocalPort, RemoteAddress, RemotePort, OwningProcess |
Format-Table -AutoSize
# Linux:查看连接详情
ss -tnp sport = :4321
# 输出所有与 4321 端口相关的连接(包括 LISTEN 和 ESTABLISHED)
端口转发(开发调试用)
# PowerShell:只在本机回环地址上将 8080 转发到 4321(需要管理员)
netsh interface portproxy add v4tov4 listenaddress=127.0.0.1 listenport=8080 connectaddress=127.0.0.1 connectport=4321
# 查看已有的转发规则
netsh interface portproxy show all
# 删除转发
netsh interface portproxy delete v4tov4 listenaddress=127.0.0.1 listenport=8080
# Linux:只绑定回环地址,避免意外暴露到局域网
socat TCP-LISTEN:8080,bind=127.0.0.1,reuseaddr,fork TCP:127.0.0.1:4321
# 或者把本机 8080 安全转发到远程主机上的 4321
ssh -L 127.0.0.1:8080:127.0.0.1:4321 user@remote-host
常见问题排查
"端口已被占用" (EADDRINUSE)
# 1. 查出是谁
Get-NetTCPConnection -LocalPort 4321 -State Listen |
Select LocalPort, OwningProcess, @{N='Name';E={(Get-Process -Id $_.OwningProcess).Name}}
# 2. 判断要不要杀
# - 如果是自己之前没关干净的 node 进程 → 杀
# - 如果是系统服务 → 换个端口
# 3. 杀掉后重新启动
$listenerPids = Get-NetTCPConnection -LocalPort 4321 -State Listen |
Select-Object -ExpandProperty OwningProcess -Unique
$listenerPids | ForEach-Object { Stop-Process -Id $_ }
npm run dev
"Connection refused"
目标地址和端口没有接受连接。可能的原因:
- 服务没启动
- 服务崩了
- 服务监听在不同地址(比如 127.0.0.1 vs 0.0.0.0)
- 防火墙或代理主动拒绝了连接
# 确认端口确实没人监听
Test-NetConnection localhost -Port 4321
# TcpTestSucceeded: False → 确实没人监听
# 检查服务进程是否还活着
Get-Process -Name node -ErrorAction SilentlyContinue
"Permission denied" (Linux)
# 绑定 1024 以下的端口通常需要 root 或 CAP_NET_BIND_SERVICE
# 开发环境优先使用 8080 等高端口;生产环境通常由反向代理监听 80/443
sudo lsof -nP -iTCP:80 -sTCP:LISTEN
# 确实需要时才给指定二进制设置 capability
node_path=$(readlink -f "$(command -v node)")
sudo setcap 'cap_net_bind_service=+ep' "$node_path"
getcap "$node_path"
# 这会影响所有经该 Node 二进制启动的程序;不再需要时移除
sudo setcap -r "$node_path"
TIME_WAIT 堆积
TIME_WAIT 是已关闭 TCP 连接的状态,通常出现在主动关闭连接的一方。它保护的是旧连接的四元组,不表示整个本地端口被统一锁定,也不必然阻止服务重新监听。
# 查看 TIME_WAIT 状态的连接
Get-NetTCPConnection -State TimeWait | Where-Object LocalPort -eq 4321
# 排障时同时查看本地/远程地址和端口,确认是哪一批连接
Get-NetTCPConnection -State TimeWait |
Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort
# Linux 查看 TIME_WAIT 连接
ss -tan state time-wait
不要为了 TIME_WAIT 修改 net.ipv4.tcp_fin_timeout:该参数控制的是孤立连接在 FIN_WAIT_2 状态停留的时间,并不解决 TIME_WAIT。开发服务重启失败时,应先确认是否仍有监听进程、是否绑定了相同地址,以及应用是否正确配置了 socket 重用。
多个进程监听同一端口?
同一协议、地址和端口通常只有一个监听 socket,但以下情况可能看到多个进程或多条记录:
- 父进程把同一个监听 socket 继承给子进程
- 程序使用了
SO_REUSEPORT - IPv4 和 IPv6 分别监听
- 同一端口分别绑定在不同的本地地址上
# 查看是不是 IPv4/IPv6 各一个
Get-NetTCPConnection -LocalPort 4321 -State Listen |
Select-Object LocalAddress, LocalPort, OwningProcess
# 0.0.0.0:4321 和 [::]:4321 是两条不同地址族的监听记录;
# 是否属于同一程序要以 OwningProcess 为准
推荐的个人习惯
- 开发前:先查一下目标端口有没有监听者 →
Get-NetTCPConnection -LocalPort 4321 -State Listen - 开发中:服务起不来先排查端口 → 看报错信息里的 EADDRINUSE
- 开发后:确认服务正常停止 →
Test-NetConnection localhost -Port 4321应该返回 False - 写脚本时:启动服务后加 wait_port 确认就绪再继续后续操作