换了一家公司,新公司的网络环境只支持Windows,于是从Mac切换到了Windows。由此遇到了很多很多的问题,在此记录一些比较典型的问题。
问题记录
我要启动一个Docker容器,想要使用9595端口。使用的命令如下:
docker run -d ^
-p 9595:80 ^
--name doc-server ^
-v //d/docker_share/doc-server/helper:/usr/share/nginx/html/helper:ro ^
-v //d/docker_share/doc-server/config/nginx.conf:/etc/nginx/nginx.conf:ro ^
nginx:1.29.0
运行得到了异常结果:
docker: Error response from daemon: Ports are not available: exposing port TCP 0.0.0.0:9595 -> 0.0.0.0:0: listen tcp 0.0.0.0:9595: bind: An attempt was made to access a socket in a way forbidden by its access permissions.
从字面上来看,就是绑定9595接口失败了。因为9595端口较高,一般不会是系统保留,所以我们排查一下是不是其他程序占用了该端口:
netstat -ano | findstr "9595"
排查结果:
找不到占用了该端口的程序。
既然不是应用程序占用,会不会是系统把端口保留了?使用命令排查一下:
netsh interface ipv4 show excludedportrange protocol=tcp
排查结果如下:
可以发现,9595端口确实被系统占用了。
查询资料了解到这种情况一般是由于:
Windows 10/11 会为系统服务(Hyper-V、RPC、某些安全软件、WSL2、VPN、Edge 浏览器的调试端口等)预留一批端口,以防被其他程序抢占。
造成 9595 这种高位端口被保留的常见原因:
- Hyper-V / WSL2:Docker Desktop 会依赖它,启动时会预留一批高端口。
- Windows 服务(WinRM、Net.TCP Port Sharing Service 等):这些可能预留固定区间。
- VPN 或代理软件:有些会锁定一批端口用作隧道连接。
- 曾经被用作 RPC 端口范围:RPC Endpoint Mapper 会随机分配高位端口给服务,系统会提前标记。
在这种情况下,如果一定要使用这个端口,可以使用以下的命令:
netsh int ipv4 delete excludedportrange protocol=tcp startport=9595 numberofports=1
端口排查脚本的运行
基于上面的各种步骤,我写了一个脚本以方便排查过程。
# 让用户输入端口号
$Port = Read-Host "请输入要检查的端口号"
Write-Host "正在检查端口 $Port ..." -ForegroundColor Cyan
# 1. 检查端口是否在保留范围内
$excluded = netsh interface ipv4 show excludedportrange protocol=tcp |
ForEach-Object {
$_.Trim() } |
Where-Object {
$_ -match '^\d+' } |
ForEach-Object {
$parts = $_ -split '\s+'
[PSCustomObject]@{
StartPort = [int]$parts[0]
EndPort = [int]$parts[1]
}
}
$inRange = $excluded | Where-Object {
$Port -ge $_.StartPort -and $Port -le $_.EndPort }
if ($inRange) {
Write-Host "! 端口 $Port 位于系统保留区间: $($inRange.StartPort)-$($inRange.EndPort)" -ForegroundColor Yellow
} else {
Write-Host "- 端口 $Port 不在保留区间" -ForegroundColor Green
}
# 2. 检查是否有进程占用
$connection = Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue
if ($connection) {
$pid = $connection.OwningProcess
$proc = Get-Process -Id $pid -ErrorAction SilentlyContinue
Write-Host "! 端口 $Port 被进程占用:" -ForegroundColor Red
Write-Host (" 进程名: {0}" -f $proc.ProcessName)
Write-Host (" PID: {0}" -f $pid)
} else {
Write-Host "- 端口 $Port 当前没有进程监听" -ForegroundColor Green
}
# 3. 如果是保留状态,提示可能来源
if ($inRange -and -not $connection) {
Write-Host "`n可能的保留来源:" -ForegroundColor Cyan
Write-Host " - Hyper-V / WSL2(Docker Desktop 依赖它)"
Write-Host " - VPN 或代理软件"
Write-Host " - Windows RPC / WinRM 服务"
Write-Host " - 某些杀毒软件或公司策略"
}
可以将它保存成一个.ps1格式的脚本文件,这里以port_check.ps1为例。注意:由于Windows的PowerShell编码问题,如果使用Windows自带的版本(未自行升级),编码需要保存为GBK。要运行它,有以下几种方法:
使用powershell窗口运行
因为要使用管理员权限,所以对着脚本使用右键菜单中的"使用PowerShell运行"是无效的,使用时会直接闪退且没有任何错误提示。我们只能手动在开始菜单中搜索PowerShell,使用管理员身份运行。
在窗口中使用以下命令:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
D:\tmp\port-check-scripts\port_check.ps1
这其中,Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass用于对PowerShell窗口进行提权,D:\tmp\port-check-scripts\port_check.ps1是脚本的路径,按照实际情况进行替换。
使用外部CMD窗口运行
因为右键菜单中不包含以管理员身份运行PowerShell的命令,因此我又写了一个cmd脚本来唤起管理员身份的PowerShell窗口以运行这个文件。
@echo off
:: 检查是否是管理员权限
net session >nul 2>&1
if %errorlevel% neq 0 (
echo 正在以管理员权限重新启动...
powershell -Command "Start-Process cmd -ArgumentList '/c \"%~f0\"' -Verb RunAs"
exit /b
)
:: 获取当前脚本所在目录(自动加引号防止空格问题)
set "CurrentDir=%~dp0"
:: 检查 check-port.ps1 是否存在
if not exist "%CurrentDir%port_check.ps1" (
echo 找不到 "%CurrentDir%port_check.ps1"
pause
exit /b
)
:: 运行 PowerShell 脚本
powershell -NoExit -ExecutionPolicy Bypass -File "%CurrentDir%port_check.ps1"
将其保存为.cmd文件,如run.cmd,与刚刚的port_check.ps1放在同一目录下,双击运行就行了。
运行效果如图:
