跳到主要内容

Windows 端口被占用

· 阅读需 9 分钟

问题的解决过程

今天碰到了一个很诡异的端口占用问题。

项目使用的端口是 9052,在启动的时候出现了下面的提示。

Description:
Web server failed to start. Port 9052 was already in use.

Action:

Identify and stop the process that's listening on port 9052 or configure this application to listen on another port.

这是一个很常见的端口占用问题,通过下面的命令就能解决了。

netstat -aon | findstr "9052"
taskkill /T /F /PID 1234

但是,在我执行 netstat -aon | findstr "9052" 之后,什么都没有输出。

奇了怪了,不应该呀。然后用 TCPView 搜索 9052 端口占用情况,也什么都没有输出。

然后我把我的问题丢给了 AI。

netstat -ano | findstr 9052  查找不到占用 9052 的应用程序,但是在启动 springboot 的时候,出现了以下提示,我应该如何排查问题? 找出占用 9052 的应用程序?

Description:

Web server failed to start. Port 9052 was already in use.

Action:

Identify and stop the process that's listening on port 9052 or configure this application to listen on another port.

AI 给了我一些的建议

  1. 用管理员运行 cmd,然后再主席 netstat 命令

  2. 使用 TCPView 软件查看

  3. 检查是否是被 IPv6 占用

    netstat -ano -p tcp | findstr 9052
    netstat -ano -p tcpv6 | findstr 9052
  4. 使用 PowerShell 执行下面的命令 (好使)

    Get-NetTCPConnection -LocalPort 9052 | Format-Table -Property LocalAddress, LocalPort, State, OwningProcess

    # 然后查看进程详情
    Get-Process -Id <PID>

    得到下面的输出

    Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
    ------- ------ ----- ----- ------ -- -- -----------
    2865 686 227392 34016 204.83 16184 1 Foxmail

最后查出来是 FoxMail 占用了 9052 端口。

思考

那现在的问题是为什么 netstat 没有结果输出, Get-NetTCPConnection -LocalPort 9052 却可以找到占用 9052 端口的应用呢?

我继续向 AI 追问。

为什么 netstat -ano | findstr 9052 没有结果输出,而 Get-NetTCPConnection -LocalPort 9052 | Format-Table -Property LocalAddress, LocalPort, State, OwningProcess 却可以找出占用  9052 端口的应用?

AI 的解释是 2 个命令的工作原理不一样。

netstat 比较老,是从 TCP/IP 协议栈的缓存中读取数据,数据不是实时的,有刷新延迟。在某些情况下,短连接或者刚建立的连接可能不会立即显示。

Get-NetTCPConnection 直接查询内核, 更准确及时。

还有一个问题,Tcpview.exe 也是从内核中读取数据,为什么当时我也没有找到呢?

继续向 AI 追问

那为什么 Tcpview.exe 没有读取到数据,Get-NetTCPConnection -LocalPort 9052 | Format-Table -Property LocalAddress, LocalPort, State, OwningProcess 却读到了数据?

AI 给出了几个可能的原因:

  1. Tcpview.exe 设置不对,如监听的协议,过滤器等设置的有问题。
  2. TCPView 的刷新时机问题,虽然都是从内核读取数据,但是 Tcpview.exe 还是会有刷新时间间隔的。默认是 1 秒。
  3. 没有以管理员权限启动 Tcpview.exe, 但是却以管理员权限启动 PowerShell。
  4. 使用问题,我在搜索的时候只输入了 9052,但是 TCPView 的显示可能是 *:9052. 我应该输入 :9052

但是现在这个问题已无法复现了,无法得知具体是哪一个原因。只能等到后续这个问题再次出现才能知道原因了。

那么,如果这个问题再次出现,我应该如何执行什么命令,来确定它的根本问题?

继续就这个问题向 AI 追问。

由于 netstat -ano | findstr 9052 和 TCPView 查询不出数据,Get-NetTCPConnection -LocalPort 9052 | Format-Table -Property LocalAddress, LocalPort, State, OwningProcess 却可以找出占用  9052 端口的应用的问题已无法复现,那么下次这个问题再次出现,我应该执行什么命令来确定这个问题的根本原因?

AI 给我返回了一个 .ps1 文件,是的,一个 .ps1 文件,之前我从来没有接触过 ps1 的任何语法。

我粗略的看了下这个 .ps1 文件,用到的语法大概是定义变量/方法,执行方法,搭配一些 if-else 语句。

然后我给 AI 提问,让它给我介绍下这些语法。

我之前从来没接触过 ps1 脚本请向我解释 ps1 中如何定义变量,方法,如何执行方法,if-else 语句的语法。

PowerShell 脚本学习

.ps1 语法学习时间

变量

  1. $name = value 来定义变量

  2. $arr = @("apple","banana","orange") 定义数组

  3. 定义哈希表

     $person = @{
    Name = "wangzhy"
    Age = 29
    City = "zhuhai"
    }
  4. 定义布尔值

     $isActive = $true
    $isDeleted = $false
  5. 特殊变量

    • $_
    • $?
    • $null
    • $true
    • $false
    • $HOME
    • $PSVersionTable
  6. 变量的使用

    • 双引号内可以直接插入变量 "my name is $name" => my name is wangzhy
    • 单引号内不会替换变量 'my name is $name' => my name is $name

函数

function name {
# function body
}

在函数体内通过 param($name) 获取参入的参数。默认按照参数传递顺序进行赋值,也可以指定。

function add {
param($a,$b)
Write-Host "a = $a"
Write-Host "b = $b"
return $a + $b
}

Write-Host " a + b = " (add 2 10) # a=2,b=10
Write-Host " a + b = " (add -b 2 -a 10) # a=10,b=2

给函数参数设置默认值 param($a,$b = 11) 如果没有给 b 传递值,那么 b 的默认值是 11。

条件判断

if(conditon){

} elseif(conditon) {

} else {

}

比较运算符

  • -eq

  • -ne

  • -gt

  • -ge

  • -lt

  • -le

  • -eq

  • -ceq 忽略大小写

  • -like 模糊匹配

  • -math 正则匹配

  • -and

  • -or

  • -not

三元运算符 $result = ($age -ge 18) ? "成年" : "未成年"

$() 的用途

  • 用于访问对象的属性或方法 $($process.Id) 执行 $process 对象的 Id 属性
  • 执行复杂表达式 $($a + $b)
  • 命令替换 Write-Host "当前时间: $(Get-Date -Format 'HH:mm:ss')"
  • 嵌套属性访问 $($connection[0].LocalPort)

.ps1 脚本文件的格式要是 **UTF-8 with BOM** 的,不然的话,如果包含中文,会出现报错。

管道符 | 的使用:将左边命令的输出传递给右边命令的输入。

  • Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 获取进程 → 排序 → 选择前 5 个
  • 123 | Set-Clipboard 将 123 输出到剪贴板

最后得到一个 .ps1 文件

# ============================================
# 指定端口(通过 -port 传入,默认是 9052 端口)占用问题诊断脚本
# 当问题出现时立即运行此脚本
# ============================================

param($port = 9052)
$timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
$logFile = "port_diagnosis_$timestamp.txt" # 日志文件

function Run-Write-Log {
param($Command)
$CommandResult = iex $Command
$Message = $CommandResult
if ($Message -isnot [string]) {
$Message = $Message | Out-String
}

Write-Host "========== $Command ==========" -ForegroundColor "GREEN"
Write-Host $Message
"========== $Command ==========" | Out-File -FilePath $logFile -Append -Encoding UTF8
$Message | Out-File -FilePath $logFile -Append -Encoding UTF8
return $CommandResult
}

$ignore = Run-Write-Log "netstat -ano | findstr :$port"
$cr = Run-Write-Log "Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue"

if ($null -ne $cr) {
# 这里要对 $($conn.OwningProcess) 去重
foreach ($conn in ($cr | Select-Object -Property OwningProcess -Unique)) {
$ignore = Run-Write-Log "Get-Process -Id $($conn.OwningProcess)"
}
}

# 通过 vscode 打开这个日志文件
code $logFile