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
相关推荐
ACP广源盛139246256735 小时前
IX8024与科学大模型的碰撞@ACP#筑牢科研 AI 算力高速枢纽分享
运维·服务器·网络·数据库·人工智能·嵌入式硬件·电脑
Empty-Filled6 小时前
AI生成测试用例功能怎么测:一个完整实战案例
网络·人工智能·测试用例
码云数智-大飞7 小时前
本地部署大模型:隐私安全与多元优势一站式解读
运维·网络·人工智能
jinanwuhuaguo7 小时前
(第二十九篇)OpenClaw 实时与具身的跃迁——从异步孤岛到数字世界的“原住民”
前端·网络·人工智能·重构·openclaw
汤愈韬7 小时前
三种常用 NAT 的经典案例
网络协议·网络安全·security
等风来不如迎风去8 小时前
【win11】最佳性能:fix 没有壁纸,一直黑屏
网络·人工智能
Harvy_没救了8 小时前
【网络部署】 Win11 + VMware CentOS8 + Nginx 文件共享服务 Wiki
运维·网络·nginx
汤愈韬8 小时前
NAT Server 与目的Nat
网络·网络协议·网络安全·security
2401_873479409 小时前
断网时如何实时判断IP归属?嵌入本地离线库,保障风控不中断
运维·服务器·网络
7ACE10 小时前
Wireshark TS | TLP 超时时间
网络·网络协议·tcp/ip·wireshark·tcpdump