You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

路由器后无入站权限的客户端如何实现P2P直连?

实现NAT后客户端A与B的P2P直连方案(Python Socket版)

核心原理:UDP打洞

TCP的NAT穿透实现难度极高,绝大多数场景下用UDP打洞就能解决问题。核心逻辑是:利用NAT的端口映射规则,让双方同时向对方的公网地址发送UDP数据包,触发NAT建立双向端口映射,从而绕过服务器实现直连。

步骤拆解

1. 服务器S的功能实现

服务器仅做地址中介,负责接收客户端的公网地址注册,再将双方地址互相转发,不处理任何业务数据。

import socket

def run_server(host='0.0.0.0', port=12345):
    # 创建TCP套接字接收客户端注册
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((host, port))
    server_socket.listen(2)

    print("等待客户端连接...")
    # 接收客户端A连接并记录公网地址
    conn_a, addr_a = server_socket.accept()
    print(f"客户端A已连接,公网地址:{addr_a}")
    # 接收客户端B连接并记录公网地址
    conn_b, addr_b = server_socket.accept()
    print(f"客户端B已连接,公网地址:{addr_b}")

    # 互相转发对方的公网地址
    conn_a.send(f"{addr_b[0]}:{addr_b[1]}".encode())
    conn_b.send(f"{addr_a[0]}:{addr_a[1]}".encode())

    # 完成中转后关闭连接
    conn_a.close()
    conn_b.close()
    server_socket.close()
    print("地址转发完成,服务器退出")

if __name__ == "__main__":
    run_server()

2. 客户端A/B的实现逻辑

两个客户端逻辑完全一致,只需同时运行:

  • 先连接服务器获取对方公网地址
  • 同时向对方公网地址发送UDP数据包完成打洞
  • 成功打洞后直接通过UDP收发数据
import socket
import threading

def receive_udp(udp_socket):
    # 持续监听UDP数据
    while True:
        data, addr = udp_socket.recvfrom(1024)
        print(f"收到来自{addr}的消息:{data.decode()}")

def run_client(server_host='服务器公网IP', server_port=12345):
    # 1. 连接服务器获取对方地址
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket.connect((server_host, server_port))
    peer_addr_str = tcp_socket.recv(1024).decode()
    peer_host, peer_port = peer_addr_str.split(':')
    peer_port = int(peer_port)
    print(f"获取到对方地址:{peer_host}:{peer_port}")
    tcp_socket.close()

    # 2. 创建UDP套接字用于打洞和通信
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_socket.bind(('', 0))  # 绑定随机端口

    # 启动线程监听UDP数据
    threading.Thread(target=receive_udp, args=(udp_socket,), daemon=True).start()

    # 3. 发送打洞包,直到建立直连
    print("正在尝试建立直连...")
    while True:
        # 先发送打洞请求包
        udp_socket.sendto("打洞请求".encode(), (peer_host, peer_port))
        user_input = input("输入消息发送(输入q退出):")
        if user_input.lower() == 'q':
            break
        udp_socket.sendto(user_input.encode(), (peer_host, peer_port))

    udp_socket.close()

if __name__ == "__main__":
    run_client()

关键注意事项

  • 必须同时发起打洞:仅一方发送数据包时,NAT会丢弃对方的入站包,双方需同时向对方发送UDP包。
  • NAT类型限制:该方案仅适用于锥型NAT(绝大多数家用路由器属于此类),对称型NAT无法通过此方法打洞,这种场景下无法绕过服务器中转。
  • 服务器要求:服务器必须拥有公网IP,且端口12345需开放防火墙权限。
  • 客户端端口:家用路由器默认允许UDP端口的出站/入站,无需额外配置。

内容的提问来源于stack exchange,提问作者Fyaz Manknojiya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 02:45:20