問題概要#
JSBSimスクリプト実行時に “Socket bind error: Address already in use” エラーが発生し、UDP通信が開始できません。
原因#
ポート5550が既に別のプロセスによって使用されていることが原因です。
詳細な原因#
- 前回実行の残骸: JSBSimスクリプトを強制終了(Ctrl+C)した際、ポートが解放されずに残った
- FlightGearが起動中: FlightGearがポート5550でリッスンしている
- TIME_WAIT状態: Windowsではプロセス終了後もポートが数秒~数分間保持される
- 他のアプリケーション: 別のアプリケーションがポート5550を使用している
よくあるケース:
- Ctrl+Cでスクリプトを強制終了後、すぐに再実行
- FlightGearを起動したまま、JSBSimスクリプトを再実行
- 前回のJSBSimプロセスがバックグラウンドで動作中
解決方法#
Windows環境#
ステップ1: ポート5550を使用中のプロセスを確認
netstat -ano | findstr 5550出力例:
UDP 0.0.0.0:5550 *:* 12345ステップ2: プロセスIDからプロセス名を確認
tasklist | findstr 12345出力例:
python.exe 12345 Console 1 50,000 Kステップ3: プロセスを終了
taskkill /PID 12345 /Fまたは、タスクマネージャーから該当プロセスを終了。
Linux/Mac環境#
ステップ1: ポート5550を使用中のプロセスを確認
lsof -i :5550出力例:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python 12345 user 3u IPv4 123456 0t0 UDP *:5550ステップ2: プロセスを終了
kill -9 12345スクリプトでの自動化(Python)#
SO_REUSEADDRオプションを使用:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# ポート再利用を許可
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
sock.bind(('localhost', 5550))
print("✅ ポート5550: バインド成功")
except OSError as e:
print(f"❌ ポート5550: バインド失敗 - {e}")
exit(1)利点:
- TIME_WAIT状態のポートを再利用可能
- 強制終了後すぐに再実行できる
注意:
- SO_REUSEADDRは既存プロセスが完全に終了している場合のみ有効
- プロセスが実行中の場合は、手動で終了する必要がある
予防策#
1. 適切な終了処理の実装#
try-finallyでソケットをクローズ:
import socket
sock = None
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('localhost', 5550))
# シミュレーション実行
while True:
# ... UDP送信処理 ...
pass
finally:
if sock:
sock.close()
print("ソケットをクローズしました")2. シグナルハンドラの実装#
Ctrl+C時に確実にソケットをクローズ:
import socket
import signal
import sys
sock = None
def signal_handler(sig, frame):
"""Ctrl+C時の処理"""
print("\nシャットダウン中...")
if sock:
sock.close()
print("ソケットをクローズしました")
sys.exit(0)
# シグナルハンドラを登録
signal.signal(signal.SIGINT, signal_handler)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('localhost', 5550))
try:
while True:
# ... シミュレーション実行 ...
pass
except Exception as e:
print(f"エラー: {e}")
finally:
if sock:
sock.close()3. 起動スクリプトに既存プロセスのクリーンアップを追加#
自動的にポート使用中のプロセスを終了:
import subprocess
import re
def cleanup_port(port=5550):
"""ポート使用中のプロセスを終了"""
try:
# Windows
result = subprocess.run(['netstat', '-ano'], capture_output=True, text=True)
for line in result.stdout.split('\n'):
if f':{port}' in line and 'LISTENING' in line or 'UDP' in line:
match = re.search(r'\s+(\d+)$', line)
if match:
pid = match.group(1)
print(f"ポート{port}を使用中のプロセス(PID: {pid})を終了します...")
subprocess.run(['taskkill', '/PID', pid, '/F'], capture_output=True)
print("✅ プロセスを終了しました")
return True
except Exception as e:
print(f"クリーンアップ失敗: {e}")
return False
# スクリプト開始前にクリーンアップ
cleanup_port(5550)まとめ#
Socket bind error問題は、ポートが既に使用中であることが原因です。
原因:
- 前回実行のJSBSimプロセスが残っている
- FlightGearがポート5550でリッスン中
- TIME_WAIT状態でポートが保持されている
解決策:
- netstat/lsofでポート使用中のプロセスを確認
- taskkill/killでプロセスを終了
- SO_REUSEADDRオプションでポート再利用を許可
予防策:
- try-finallyで確実にソケットをクローズ
- シグナルハンドラでCtrl+C時の処理を実装
- 起動スクリプトに既存プロセスのクリーンアップを追加
SO_REUSEADDRオプションとシグナルハンドラを組み合わせることで、確実にポート問題を回避できます。
関連記事#
- 【トラブル備忘】Windows FirewallによるUDP通信ブロック - FlightGear接続失敗(記事E-16)
- 【トラブル備忘】FlightGearに機体が表示されない - UDP通信ポート番号不一致(記事E-3)
© 2025 Yaaasoh. All Rights Reserved.