空きポートの検出

空きポートの検出

[1] TCP の空いている (listen 可能な) ポート番号を取得したいことがあります。

[2] 例えば CI などでテストプログラム用のサーバープロセスを自動起動させたいとき、 ポート番号を決定する必要があります。 他のテストと干渉しないように固定ポートではなく、 動的に空きポートを探したいです。

[3] 結論としては空きポートを事前に知る可搬的な方法はないみたいです。

[4] 適当なポート番号を選んで実際に listen してみて、 エラーになったらポート番号を変えて再試行、 という方法がよく採られているようです。

while (true) {
  $port = get_random_port
  $socket = listen $port
  if (not $socket is failure) {
    break;
  }
}

[5] ポート番号の決定と実際の listen が同じプロセスならそれでいいですが、 別の時 (例えばテストスクリプトでポート番号を決定して、 既製品のサーバーソフトウェアの設定ファイルを生成して起動する場合) には、 listen に成功したらそのソケットを一旦 close してからサーバープロセスを起動することになりますが、 その間に他のプロセスがポート番号を奪ってしまうリスクが少しあります。

while (true) {
  $port = get_random_port
  $socket = listen $port
  if (not $socket is failure) {
    break;
  }
}
close $socket
run ("path/to/server", "--port", $port)

[6] そのリスクを気にするなら、ポート番号を決定 → サーバーを起動 → 起動失敗したらポート番号決定からやり直す、 となりますが、サーバー起動のオーバーヘッドが大きいときはしんどいです。

while (true) {
  $port = get_random_port
  $result = run ("path/to/server", "--port", $port)
  if (not $result is failure) {
    break;
  }
}

[7] 自動化されたテストのようなたまに失敗しても被害が出ないくらいのものなら、 現実的な競合条件のリスクを考えれば、 >>5 の方法で十分機能するみたいです。

[8] ポート番号は [0, 65535] から選べますが、 特権ポートを使うと特権ユーザー (root) 以外が実行できなくなってしまいます。 従って [0, 1023] は避けるべきです。

[9] なんとなく境界値は危うい気がするので、 102465535 (0xFFFF) も避けるのが無難そうです。

[10] Webブラウザーからサーバーに接続するテストを想定すると、 Webブラウザーfetch できない bad ports も避けなければなりません。