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

使用STUN获取外网IP端口后,UDP P2P通信失败求助

UDP P2P通信打洞失败问题

我尝试通过UDP实现P2P通信,借助pystun3获取到外网IP和端口,但客户端B使用该IP和端口发送数据时,始终无法到达客户端A。我清楚不能关闭socket,否则NAT会重新分配端口,但即使保持socket开启,客户端A仍无法接收数据。

代码片段

客户端A(查询外网IP和端口)

import socket
from stun import get_nat_type

# 创建socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(20)
s.setsockopt(socket.SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(("0.0.0.0",15000))

# 查询STUN服务器获取外网IP和端口
nat_type, nat = get_nat_type(s, "0.0.0.0", 15000,
                                stun_host=None, stun_port=3478)
external_ip = nat['ExternalIP']
external_port = nat['ExternalPort']

# 输出STUN数据并监听客户端B数据
print("Punch hole: ('{0}',{1})".format(external_ip, external_port))
print("listening port for incoming data")
print(s.recvfrom(4096)[0])

客户端B(发送数据)

from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
s.sendto(b"hey!", ('<External IP of Client A>', 11224))

问题排查与解决办法

  • 端口不匹配:客户端A绑定本地端口15000,但客户端B发送的目标端口是11224,完全不对应。必须使用STUN返回的external_port作为客户端B的目标端口,而非手动填写的11224。
  • NAT类型限制:如果是对称型NAT,仅靠STUN获取的端口无法直接打洞——对称NAT会给每个不同目标IP分配独立的外网端口。这种情况需要双方同时向对方发起连接请求,触发NAT建立双向映射,或者借助第三方中继服务器中转数据。
  • 防火墙拦截:检查客户端A的本地防火墙设置,确保允许UDP端口15000的入站连接,避免数据包被拦截。
  • Socket状态保持:get_nat_type调用时会使用客户端A的socket发送STUN请求,NAT映射的是该socket的会话。必须保持这个socket持续开启,且只能用它来接收数据,不能重新创建新socket。
  • 冗余设置移除:客户端B发送单播数据,不需要设置SO_BROADCAST选项,移除该设置可避免潜在异常。

修正后的代码示例

客户端A

import socket
from stun import get_nat_type

# 创建UDP socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(20)
s.setsockopt(socket.SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(("0.0.0.0", 15000))

# 查询STUN获取外网地址
nat_type, nat = get_nat_type(s, "0.0.0.0", 15000, stun_host=None, stun_port=3478)
external_ip = nat['ExternalIP']
external_port = nat['ExternalPort']

print(f"打洞目标地址: ('{external_ip}', {external_port})")
print("等待客户端B数据...")
data, addr = s.recvfrom(4096)
print(f"收到来自{addr}的消息: {data.decode()}")

客户端B

from socket import *

s = socket(AF_INET, SOCK_DGRAM)
# 使用客户端A返回的外网IP和端口
s.sendto(b"hey!", ('客户端A实际外网IP', 客户端A STUN返回的外网端口))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 11:05:24