#!/usr/bin/env python3 """ auto_socat_bind.py ────────────────── 在本机 10.1.1.2:3333-3337 (utun0) 上监听 TCP, 并将流量转发到 127.0.0.1:同端口(已有服务监听)。 若 socat 异常退出则 5 秒后自动重启。 """ import subprocess import threading import signal import time import shutil import logging from typing import List # ─── 配置区域 ──────────────────────────────────────────────────────────────── TARGET_BIND = "10.1.1.8" # 监听地址(utun0) FORWARD_DST = "127.0.0.1" # 目标地址(旧服务) PORTS = range(4445, 4447) # 3333-3337 RETRY_DELAY = 5 # 子进程异常后延迟重启秒数 SOCAT_OPTS = "reuseaddr,fork,keepalive,nodelay" # socat 监听选项 # ──────────────────────────────────────────────────────────────────────────── logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(threadName)s] %(levelname)s: %(message)s", ) if shutil.which("socat") is None: raise SystemExit("❌ 未找到 socat,可通过 brew / apt 等安装后再运行") stop_evt = threading.Event() # 线程间退出信号 def guard_port(port: int) -> None: """守护单个 socat 进程""" cmd = [ "socat", f"TCP4-LISTEN:{port},bind={TARGET_BIND},{SOCAT_OPTS}", f"TCP4:{FORWARD_DST}:{port}", ] cmd_str = " ".join(cmd) while not stop_evt.is_set(): logging.info("启动 socat: %s", cmd_str) try: proc = subprocess.Popen(cmd) proc.wait() code = proc.returncode logging.warning("port %d socat 退出,code=%d", port, code) except Exception as exc: logging.exception("port %d socat 异常: %s", port, exc) code = -1 if stop_evt.is_set() or code == 0: break # 收到退出信号或正常退出,不再重启 logging.info("port %d %d 秒后重启", port, RETRY_DELAY) time.sleep(RETRY_DELAY) logging.info("port %d 守护线程结束", port) def main() -> None: # 捕获 Ctrl-C / SIGTERM def _signal_handler(signum, _frm): logging.info("收到信号 %s,准备退出…", signum) stop_evt.set() signal.signal(signal.SIGINT, _signal_handler) signal.signal(signal.SIGTERM, _signal_handler) threads: List[threading.Thread] = [] for p in PORTS: t = threading.Thread( target=guard_port, args=(p,), name=f"port-{p}", daemon=True, ) t.start() threads.append(t) # 等待所有守护线程退出 for t in threads: t.join() logging.info("全部端口守护线程已退出") if __name__ == "__main__": main()