feat(tools): 打洞探针与极简 rendezvous 服务器(Python POC)
- punch_probe.py: UDP 打洞探针,支持 STUN 查映射、--peer 手工配对、--server/--room 信令配对 - rendezvous.py: 极简 UDP 信令服务器,只做房间配对与地址回推,不承载媒体 - 已在 47.98.245.66 实测跨公网注册/配对可用
This commit is contained in:
1 parent
bb392507bd
commit
d659a4e7f1
2 files changed
+433
No files matched your search
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""极简 rendezvous(信令)服务器 —— POC 用
|
||||
|
||||
作用只有一个:**告诉双方「对方看到的你」是什么地址**。
|
||||
双方注册到同一个房间后,服务器把各自观察到的公网地址回推给对方,
|
||||
之后双方直接打洞,服务器不再参与(也不承载任何媒体流量)。
|
||||
|
||||
用法(跑在有公网 IP 的机器上,比如 43.140.215.244):
|
||||
py rendezvous.py --port 47999
|
||||
|
||||
客户端:
|
||||
py punch_probe.py --server <服务器IP>:47999 --room test01 --port 47800
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
|
||||
# 房间成员多久没动静就清掉
|
||||
MEMBER_TTL = 30.0
|
||||
|
||||
|
||||
def fmt(sockaddr):
|
||||
host, port = sockaddr[0], sockaddr[1]
|
||||
return "[%s]:%d" % (host, port) if ":" in host else "%s:%d" % (host, port)
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="极简 rendezvous 服务器")
|
||||
ap.add_argument("--bind", default="0.0.0.0")
|
||||
ap.add_argument("--port", type=int, default=47999)
|
||||
args = ap.parse_args()
|
||||
|
||||
# 重定向到文件时 stdout 会全缓冲,日志要等进程退出才出现;强制行缓冲
|
||||
try:
|
||||
sys.stdout.reconfigure(line_buffering=True)
|
||||
except (AttributeError, OSError):
|
||||
pass
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.bind((args.bind, args.port))
|
||||
|
||||
print("[rendezvous] 监听 udp://%s:%d" % (args.bind, args.port))
|
||||
print("[rendezvous] 房间成员 TTL = %.0fs,Ctrl+C 退出\n" % MEMBER_TTL)
|
||||
|
||||
rooms = {} # room -> [(addr, last_seen)]
|
||||
|
||||
try:
|
||||
while True:
|
||||
data, src = sock.recvfrom(2048)
|
||||
txt = data.decode("utf-8", "replace").strip()
|
||||
now = time.time()
|
||||
|
||||
if not txt.startswith("REGISTER|"):
|
||||
continue
|
||||
room = txt.split("|", 1)[1].strip()
|
||||
if not room:
|
||||
continue
|
||||
|
||||
# 清掉过期成员 + 去掉本机的旧记录(支持重复注册)
|
||||
members = [(a, t) for (a, t) in rooms.get(room, [])
|
||||
if now - t < MEMBER_TTL and a[1] != src[1]]
|
||||
members.append((src, now))
|
||||
rooms[room] = members
|
||||
|
||||
print("[rendezvous] %s 注册 room=%s (现有 %d 人) 源地址=%s"
|
||||
% (fmt(src), room, len(members), fmt(src)))
|
||||
|
||||
if len(members) >= 2:
|
||||
a1, _ = members[0]
|
||||
a2, _ = members[1]
|
||||
sock.sendto(b"PEER|" + fmt(a2).encode(), a1)
|
||||
sock.sendto(b"PEER|" + fmt(a1).encode(), a2)
|
||||
print("[rendezvous] -> 配对成功:%s <==> %s"
|
||||
% (fmt(a1), fmt(a2)))
|
||||
else:
|
||||
sock.sendto(b"WAIT", src)
|
||||
print("[rendezvous] -> 等待第二个人加入 ...")
|
||||
except KeyboardInterrupt:
|
||||
print("\n[rendezvous] 退出")
|
||||
finally:
|
||||
sock.close()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user