udp穿透的方法V2

背景是我23年写过一个udp穿透的方法,那时候手搓了一个小demo,细节见:

udp穿透的方法:https://blog.csdn.net/yeshennet/article/details/135165159

上周公司同事有在讨论这个,我又回头看了下我那时候写的脚本。现在有AI了,提了一下说明,生成了代码如下:

python 复制代码
# Server (Public IP)
import socket
import threading

clients = {}  # {client_id: (ip, port)}

def handle_client(sock):
    while True:
        try:
            data, addr = sock.recvfrom(1024)
            msg = data.decode()
            
            if msg.startswith("REGISTER:"):
                # Register client
                client_id = msg.split(":")[1]
                clients[client_id] = addr
                print(f"Registered: {client_id} -> {addr}")
                sock.sendto(b"OK", addr)
                
            elif msg.startswith("QUERY:"):
                # Query peer address
                target_id = msg.split(":")[1]
                if target_id in clients:
                    target_addr = clients[target_id]
                    # Send target address to requester
                    response = f"ADDR:{target_addr[0]}:{target_addr[1]}"
                    sock.sendto(response.encode(), addr)
                    # Notify target that someone wants to connect
                    notify = f"PEER:{addr[0]}:{addr[1]}"
                    sock.sendto(notify.encode(), target_addr)
                    print(f"Connect: {addr} <-> {target_addr}")
                else:
                    sock.sendto(b"NOT_FOUND", addr)
        except Exception as e:
            print(f"Error: {e}")

def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind(("0.0.0.0", 9999))
    print("Server started on port 9999")
    handle_client(sock)

if __name__ == "__main__":
    main()
python 复制代码
# Client A/B
import socket
import time
import threading

class UDPClient:
    def __init__(self, client_id, server_addr):
        self.client_id = client_id
        self.server_addr = server_addr
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.bind(("0.0.0.0", 0))
        self.sock.settimeout(0.5)  # Non-blocking receive with timeout
        
        self.peer_addr = None
        self.connected = False
        self.running = True
        
    def register(self):
        # Register to server
        msg = f"REGISTER:{self.client_id}"
        self.sock.sendto(msg.encode(), self.server_addr)
        print(f"Registered as {self.client_id}")
        time.sleep(0.5)
        
    def query_peer(self, peer_id):
        # Query peer address from server
        msg = f"QUERY:{peer_id}"
        self.sock.sendto(msg.encode(), self.server_addr)
        print(f"Querying {peer_id}...")
        
    def send_thread(self):
        # Thread for sending packets periodically
        last_send_time = 0
        while self.running:
            current_time = time.time()
            
            # Send packet every 1 second if not connected or as keepalive
            if self.peer_addr and (current_time - last_send_time >= 1.0):
                try:
                    msg = f"PING:{self.client_id}"
                    self.sock.sendto(msg.encode(), self.peer_addr)
                    last_send_time = current_time
                    if not self.connected:
                        print(f"Punching hole to {self.peer_addr}...")
                except Exception as e:
                    print(f"Send error: {e}")
            
            time.sleep(0.1)  # Small sleep to avoid busy loop
    
    def receive_thread(self):
        # Thread for receiving packets
        while self.running:
            try:
                data, addr = self.sock.recvfrom(1024)
                msg = data.decode()
                
                if msg.startswith("ADDR:"):
                    # Got peer address from server
                    parts = msg.split(":")
                    peer_ip = parts[1]
                    peer_port = int(parts[2])
                    self.peer_addr = (peer_ip, peer_port)
                    print(f"Got peer address: {self.peer_addr}")
                    
                elif msg.startswith("PEER:"):
                    # Server notified us that someone wants to connect
                    parts = msg.split(":")
                    peer_ip = parts[1]
                    peer_port = int(parts[2])
                    self.peer_addr = (peer_ip, peer_port)
                    print(f"Peer wants to connect: {self.peer_addr}")
                    
                elif msg.startswith("PING:"):
                    # Received ping from peer
                    if not self.connected:
                        self.connected = True
                        print(f"*** Hole punched! Connected to {addr} ***")
                    # Send pong back
                    pong_msg = f"PONG:{self.client_id}"
                    self.sock.sendto(pong_msg.encode(), addr)
                    
                elif msg.startswith("PONG:"):
                    # Received pong from peer
                    if not self.connected:
                        self.connected = True
                        print(f"*** Hole punched! Connected to {addr} ***")
                        
                elif msg.startswith("MSG:"):
                    # Normal message from peer
                    print(f"<< {msg[4:]}")
                    
            except socket.timeout:
                # Timeout, continue loop
                continue
            except Exception as e:
                if self.running:
                    print(f"Receive error: {e}")
    
    def run(self, peer_id):
        # Start threads
        recv_thread = threading.Thread(target=self.receive_thread, daemon=True)
        send_thread = threading.Thread(target=self.send_thread, daemon=True)
        
        recv_thread.start()
        send_thread.start()
        
        # Register and query peer
        self.register()
        time.sleep(1)
        self.query_peer(peer_id)
        
        # Wait for connection
        print("Waiting for connection...")
        while not self.connected:
            time.sleep(0.5)
        
        # Interactive chat
        print("You can now send messages (type 'quit' to exit):")
        try:
            while True:
                user_input = input("> ")
                if user_input.lower() == 'quit':
                    break
                if self.peer_addr:
                    msg = f"MSG:{user_input}"
                    self.sock.sendto(msg.encode(), self.peer_addr)
        except KeyboardInterrupt:
            pass
        
        self.running = False
        print("Shutting down...")

def main():
    import sys
    if len(sys.argv) != 4:
        print("Usage: python client.py <client_id> <peer_id> <server_ip>")
        print("Example: python client.py A B 1.2.3.4")
        return
    
    client_id = sys.argv[1]
    peer_id = sys.argv[2]
    server_ip = sys.argv[3]
    server_addr = (server_ip, 9999)
    
    client = UDPClient(client_id, server_addr)
    client.run(peer_id)

if __name__ == "__main__":
    main()

Usage

1. Start server on public IP:

bash 复制代码
python server.py

2. Start Client A (in LAN A):

bash 复制代码
python client.py A B <server_public_ip>

3. Start Client B (in LAN B):

bash 复制代码
python client.py B A <server_public_ip>

Key Features

  1. Non-blocking: Uses separate threads for sending and receiving
  2. Auto-retry: Sends UDP packet every 1 second when not connected
  3. Keepalive: Continues sending packets after connection for NAT mapping maintenance
  4. Python 3: Compatible with Python 3.x
  5. No Chinese: All comments and messages in English

本系列的其他文章

  1. udp穿透的方法:https://blog.csdn.net/yeshennet/article/details/135165159
  2. udp穿透的方法V2:https://blog.csdn.net/yeshennet/article/details/157170723
  3. TCP穿透的方法:https://blog.csdn.net/yeshennet/article/details/157170822
相关推荐
星恒讯工业路由器3 小时前
5G FWA技术演进与趋势
网络·5g·信息与通信·4g·5g fwa·3gpp规范·fwa趋势
星夜夏空996 小时前
网络编程(1)
服务器·网络
上海云盾-小余6 小时前
中小站点防护避坑:低价高防服务存在的各类安全短板剖析
网络·爬虫·安全·ddos
X7x58 小时前
跨厂商路由引入(重发布)实战指南:华为、华三、思科的逻辑差异与配置避坑
网络协议·信息与通信·信号处理·实施路由引入
数据知道9 小时前
邮件安全实战:SPF/DKIM/DMARC 配置与钓鱼绕过
网络·安全·web安全·网络安全·邮件安全
玩转4G物联网9 小时前
FS800DTU 新品上线|免费使用 DMP 设备管理平台,支持远程批量配置 & OTA 升级
物联网·网络协议·tcp/ip·云平台·核心板·fs800dtu·iot管理平台
王莎莎-MinerU9 小时前
MCP 解决的是工具接入,科研 Agent 还缺的是科学证据接口标准化
开发语言·网络·人工智能·深度学习·pdf·c#·php
Chloeis Syntax9 小时前
JAVAEE初阶 --- 构造HTTP请求
网络·网络协议·http·postman
砚凝霜10 小时前
软考网络工程师|第 1 章 计算机网络基础 完整备考笔记
网络·笔记·计算机网络
rcms1527026921810 小时前
NIKON 4S019-394 接口模块
网络