RoboServerを起動したときに `port 50000 is already in use` エラーが出るときの対応

## はじめに

二重起動しているわけではないのに `port 50000 is already in use` というメッセージが表示して RoboServerが終了してしまう。

実は1年半前に同じ内容でブログを書いていたのも忘れて再度書いてます。前回は `Management Console` で今回は `RoboServer` という違いはあるものの、Qiitaって、記事を増やすほどに検索性というか、後になって見落とす率高いですよね。。 Zenn に引っ越そうかな。と思ってます。

https://qiita.com/ParakeetOnTheHead/items/9e20c9fe071e2c144f56

:::note warn
以下、LLMとの会話で得た情報も織り交ぜて記載します。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/c210e1a1-2a6a-493d-bb16-4cfcd3a933a2.png)

## ポートの利用状況調査

管理者権限でコマンドプロンプトを起動して、以下のコマンドを実行しましょう。

:::note
- **管理者モードで実行した場合:** システム上のすべてのプロセスに関するPID情報を取得できます。これにより、どのプロセスがどのポートを使用しているかを正確に特定できます。
- **通常のユーザーモードで実行した場合:** 通常、自分が所有するプロセスや、一部の共通のシステムプロセスに関するPID情報しか表示されません。他のユーザーが実行しているプロセスや、管理者権限で実行されている重要なシステムプロセスについては、PIDが表示されなかったり、「PIDが見つかりません」のような表示になったりすることがあります。これは、セキュリティ上の理由から、通常のユーザーが他のプロセスの詳細情報にアクセスできないようにするためです。
:::

```powershell:netstat -aon実行
netstat -aon | findstr :50080
```

このコマンドは、すべてのアクティブな接続とリスニングポート (`-a`) を数値形式 (`-n`) で表示し、それらに関連付けられているプロセスのID (PID) (`-o`) も表示します。
`findstr :50080` でポート番号が50080の行に絞り込みます。

```powershell
C:\Users\robo>netstat -aon | findstr :50080
  TCP         127.0.0.1:53480        127.0.0.1:50080        SYN_SENT        31708
  TCP         172.19.48.1:50080      0.0.0.0:0              LISTENING       4684
  TCP         [::1]:53473            [::1]:50080            SYN_SENT        31708
```
表示される項目は順番に以下を示します。

<dl>
    <dt>プロトコル (Proto):</dt>
    <dd>TCP または UDP。</dd>
    <dt>ローカルアドレス (Local Address):</dt>
    <dd>自分のPCのIPアドレスとポート番号。0.0.0.0:50080は、任意のIPアドレスでポート50080をリッスンしている状態を示します。[::]:50080 はIPv6の場合です。</dd>
    <dt>外部アドレス (Foreign Address):</dt>
    <dd>接続相手のIPアドレスとポート番号。0.0.0.0:0や[::]:0は、リスニング状態（まだ接続がない状態）を示します。</dd>
    <dt>状態 (State):</dt>
    <dd>現在の接続の状態。LISTENINGは接続待ちの状態です。</dd>
    <dt>PID (Process ID):</dt>
    <dd>その接続またはリスニングポートを使用しているプロセスのID。上記の例では 1234 がそのプロセスIDです。</dd>
</dl>

## タスクマネージャーでプロセスを特定
特定したプロセスのPIDが分かったら、タスクマネージャーを開き、「詳細」タブに切り替えます。PID列を確認し、該当するPIDのプロセスを選択して右クリックから「サービスの表示」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090491/19b25174-f89b-4b07-b925-3a3dad5cee86.png)

## 例題：`svchost.exe` 編

### `svchost.exe` がポート50080を使用していることが特定できた場合

`svchost.exe`は、Windowsサービスを実行するための汎用的なホストプロセスであり、一つの`svchost.exe`プロセスの中で複数のサービスが動作しているのが一般的です。そのため、タスクマネージャーにはたくさんの`svchost.exe`が起動しているように見えます。

特定の`svchost.exe`プロセスを強制終了させると、**そのプロセスがホストしている複数のWindowsサービスがすべて強制的に停止**してしまいます。これは、思わぬシステム機能の停止や不安定化を招く可能性があるため、**安易な強制終了は推奨されません。**

重要なのは、**その50080ポートを使用している`svchost.exe`が、どのWindowsサービスのグループをホストしているのか**を特定することです。

### 対象の `svchost.exe` がホストするサービスを特定する方法

1.  **タスクマネージャーで確認する:**
    * タスクマネージャーを開き、「詳細」タブに切り替えます。
    * PID（プロセスID）列を表示させ、ポート50080を使用している`svchost.exe`のPIDを探します。
    * その`svchost.exe`を右クリックし、「**サービスの表示**」を選択します。
    * すると、その`svchost.exe`プロセスがホストしているサービスの一覧が表示されます。

2.  **コマンドプロンプトまたはPowerShellで確認する:**
    管理者権限でコマンドプロンプトまたはPowerShellを開き、以下のコマンドを実行します。`[特定したPID]`の部分は、ポート50080を使用している`svchost.exe`の実際のPIDに置き換えてください。
    ```bash
    # コマンドプロンプトの場合
    tasklist /svc /fi "PID eq [特定したPID]"

    # PowerShellの場合
    Get-CimInstance win32_service -Filter "ProcessId = [特定したPID]"
    ```
    これらのコマンドでも、そのPIDの`svchost.exe`がホストしているサービス名の一覧が表示されます。

### ポートを使用しているサービスを特定し、安全にポートを解放する

特定したサービスの一覧の中に、ポート50080を使用していそうなサービス（例：Web関連のサービス、リモート管理サービス、特定のアプリケーションサービスなど）や、BizRobo!に関連しそうなサービスがないか確認します。

もし、そのサービスがポート50080をリッスンしていることが明確、またはBizRobo!に関連するサービスであることが分かった場合、以下の手順でサービスを再起動または停止することを試みます。

1.  **対象サービスの特定:** 上記の方法で、ポート50080を使用している`svchost.exe`がホストしているサービスの中から、原因と思われるサービスを特定します。
2.  **サービスの再起動:** 特定したサービス名を控えておき、「サービス」アプリケーション（`services.msc`をファイル名を指定して実行、または管理ツールからアクセス）を開きます。
    * 該当するサービスを見つけ、右クリックして「**再起動**」を選択します。これにより、サービスが一時的に停止し、再度起動します。正常に再起動できれば、ポートが解放される可能性があります。
3.  **サービスの停止:** 再起動してもポートが解放されない、あるいは再起動ができない場合は、右クリックして「**停止**」を選択します。サービスが停止すれば、ポートは解放されます。サービスを停止した場合は、BizRobo!の起動後に必要に応じてサービスを再度開始してください。

### 強制終了について

サービスを特定して再起動や停止を試みてもポートが解放されない、またはどのサービスが原因か特定できないがどうしてもBizRobo!を起動したい、という場合の最終手段として、対象の`svchost.exe`プロセスを強制終了させることは可能です。

ただし、前述の通り、そのプロセスがホストしている**複数のサービスがすべて強制停止**します。これにより、以下のような影響が出る可能性があります。

* システム機能の一部が停止する（例：Windows Update関連、ネットワーク関連、特定のデバイス機能など）。
* システムが不安定になる、エラーが発生する。
* 最悪の場合、OSの再起動が必要になる。

### 推奨される手順

1.  **最も推奨:** ポートを使用している`svchost.exe`がホストするサービスを特定し、そのサービスを**正常に再起動または停止**する。
2.  **次善策:** 原因サービスが不明、または正常終了できない場合に限り、影響を理解した上で、対象の`svchost.exe`プロセスを**強制終了**する。強制終了後は、システムの動作に問題がないか注意深く監視し、必要であればOSを再起動する。
3.  **最終手段:** PC/サーバー自体を再起動する。これが最も確実にポートを解放する方法ですが、業務への影響を確認する必要があります。

まずは、`svchost.exe`がホストしているサービスの一覧を確認し、どのサービスがポートを使用している可能性が高いかを見極めることから始めてください。

### RoboServer や Management Console で使用するポートを予約設定しておく方法

ここまで記事を読んだ中で、「そもそも事前に予約しておけば、他のプロセスにポートを使用されてしまう事態は防げるのでは？」と思う方も多いでしょう。

実際、ポートを予約しておいても `svchost` などのプログラムにより勝手にポートが使われてしまうことはあるのですが、一定の効果は望めるので、以下事前予約の方法もを記述します。

**管理者権限**でコマンドプロンプトを開いて実行してください。

```powershell
REM RoboServer用の待ち受けポートの予約
netsh int ipv4 add excludedportrange protocol=tcp startport=50000 ^
    numberofports=1 store=persistent

REM Management Console用の待ち受けポートの予約
netsh int ipv4 add excludedportrange protocol=tcp startport=50080 ^
    numberofports=1 store=persistent
    
REM 設定したポートの確認
netsh int ipv4 show excludedportrange protocol=tcp
```

:::note warn 
**なぜ予約したはずのポートが使われてしまうのか？**

`netsh ... add excludedportrange ... store=persistent` コマンドは、TCP/IPのドライバーに対して「このポート番号を、動的ポート（エフェメラルポート）の割り当て対象から除外してください」と指示するものです。動的ポートとは、アプリケーションが特定のポート番号を指定せず「空いているポートを貸してください」とOSに要求した際に割り当てられるポートのことです。

しかし、`svchost.exe` の中で動作している特定のWindowsサービスは、これとは異なる動きをします。

サービスの起動タイミング: 重要なWindowsサービスは、システムの起動プロセスのかなり早い段階で起動します。
明示的なポート要求: これらのサービスは「空いているポート」を要求するのではなく、自身の構成に基づいて「ポート番号 50080 を使います」と明示的にOSに要求します。
予約の適用タイミングとの競合: `store=persistent` で設定したポート除外設定は、レジストリに書き込まれ、システムの起動時にTCP/IPスタックに読み込まれます。しかし、問題のサービスが起動してポートを確保する方が、この除外設定が全てのプロセスに対して完全に効力を発揮するよりも早い場合があるのです。
つまり、一種の「早い者勝ち」が発生している状態です。`svchost` 内のサービスが、あなたが作った予約ルールが適用される前に、目的のポートを確保してしまっている可能性が非常に高いです。

by Gemini
:::


## まとめ

今回は、Gemini と一緒に問題解決した際のメモ書きをベースに記事を書きました。
AIの出力する回答を無批判に信じることはできないものの、何かに取り組む際のセカンドオピニオンや仮説、検証対象としては非常に有用だなぁと思いますね。