サーバーを起動すると、Windowsが以下のエラーをスローします:
Only one usage of each socket address (protocol/network address/port) is normally permitted
これはLinuxのEADDRINUSEに相当するWindowsのエラーです。TCPポートは同時に1つのプロセスしかバインドできません — すでに別の何かがそのポートを占有しています。ポートが解放されるまで、アプリは起動できません。
原因
主に以下の3つが原因です:
- アプリの以前のインスタンスがクラッシュまたは正常にシャットダウンされず、ポートがロックされたまま残っている
- システムサービス(IIS、SQL Server、Apache、Hyper-V仮想スイッチ)がWindows起動時にポートを取得した
- Windows自身がポート範囲を予約した — Hyper-V、WSL2、Docker Desktopを有効にした後に発生することがある
手順1:ポートを使用しているプロセスを特定する
管理者としてコマンドプロンプトを開き、以下を実行します:
netstat -ano | findstr :3000
3000を実際のポート番号に置き換えてください。出力は以下のようになります:
TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 12345
最後の列がPID(プロセスID)です。そのPIDに対応するプログラムを確認するには:
tasklist /FI "PID eq 12345"
出力例:
Image Name PID Session Name Session# Mem Usage
========================= ======== ================ =========== ============
node.exe 12345 Console 1 52,340 K
これでポートを占有しているプロセスが正確にわかります。
手順2:プロセスを終了する
taskkill /PID 12345 /F
/Fフラグは強制的に即時終了させます。アクセスが拒否されましたと表示される場合は、昇格されたプロンプトが必要です — スタートメニューを右クリックして**Windows Terminal(管理者)またはコマンドプロンプト(管理者)**を選択してください。
PowerShellのワンライナー
PowerShellを使用する場合は、以下の1行でプロセスを検索して終了できます:
Stop-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess -Force
終了する前に対象プロセスを確認したい場合:
Get-NetTCPConnection -LocalPort 3000 | Select-Object LocalPort, OwningProcess,
@{n='ProcessName'; e={(Get-Process -Id $_.OwningProcess).Name}}
出力例:
LocalPort OwningProcess ProcessName
--------- ------------- -----------
3000 12345 node
特殊ケース:Windowsの予約済みポート範囲
Hyper-V、WSL2、Docker Desktopを有効にすると、Windowsは起動時に動的ポートのブロックを予約します。ポートが予約済みブロック内にある場合、プロセスを終了しても解決しません — OSがネットワークスタックレベルでブロックしているためです。
Windowsが予約しているポートを確認するには:
netsh interface ipv4 show excludedportrange protocol=tcp
出力の中に該当ポートが見つかった場合は、予約範囲外のポートに切り替えてください。または、動的ポート範囲をリセットすることもできます:
netsh int ipv4 set dynamicport tcp start=49152 num=16384
変更後はWindowsを再起動してください。注意点として、Hyper-VとDockerは49152〜65535の範囲だけでなく、5000〜9000の範囲のブロックも予約することがあります。ポートが空いていると仮定せず、必ず自分のマシンの実際のnetsh出力で確認してください。
手順3:修正を確認する
プロセスを終了した後、ポートが解放されたことを確認します:
netstat -ano | findstr :3000
出力がなければポートは使用可能です。サーバーを起動すれば、今度はエラーなくバインドできるはずです。
何も表示されずTIME_WAITエントリが表示される場合は?
TCP 0.0.0.0:3000 0.0.0.0:0 TIME_WAIT 0
これは最近閉じた接続がまだ排出中の状態です。30〜60秒待ってから再試行するか、一時的に別のポートに切り替えてください。
予防策
アプリがポートを解放せずにクラッシュするために繰り返し発生する場合:
- Node.js:終了前にHTTPサーバーを閉じるよう
SIGTERM/SIGINTを明示的に処理します。pm2で実行することも有効で、再起動とクリーンアップを自動的に管理します。 - Python:
bind()を呼び出す前にソケットにSO_REUSEADDRを設定します。Flaskの開発サーバーはデフォルトでこれを行い、Gunicornも本番環境で正しく処理します。 - Java / .NET:クラッシュ時でもポートが解放されるよう、
server.close()(またはフレームワーク相当のメソッド)を呼び出すシャットダウンフックを追加します。
設定にポートをハードコードする前に、すでに使用中のポートを確認してください:
netstat -ano | findstr LISTENING
開発マシンでは、SQL Server(1433)、IIS(80/443)、PostgreSQL(5432)がバックグラウンドで静かに動いていることがよくあります。すでに使われているポートを選んでしまうと、実行時まで問題が先送りされるだけです。

