当前位置: 首页 > news >正文

通过公网STUN服务器实现UDP打洞

通过公网STUN服务器实现UDP打洞

    • 一、背景介绍
    • 二、原理详解
      • 1、NAT的工作原理
      • 2、STUN服务器的作用
      • 3、UDP打洞的核心思想
    • 三、NAT类型对比详解
    • 四、测试结果分析
    • 五、详细操作步骤
      • 1、环境准备
      • 2、代码
      • 3、完整操作流程
        • 3.1、在两台设备上运行脚本
        • 3.2、交换公网地址信息
        • 3.3、同步开始打洞
        • 3.4、验证连接
    • 六、实际应用场景
    • 七、总结

一、背景介绍

在当今互联网环境中,很多设备都位于NAT(网络地址转换)设备之后,这导致设备之间无法直接建立P2P连接。UDP打洞技术正是为了解决这一问题而诞生的。

什么是UDP打洞?
想象一下,两个人都住在不同的公寓楼里(NAT后面),他们想直接通话而不是通过中间人转接。UDP打洞就像是通过一个公共的"介绍人"(STUN服务器)让双方知道对方的"窗户位置"(公网IP和端口),然后同时向对方的窗户喊话,从而建立直接的联系。

二、原理详解

1、NAT的工作原理

NAT设备就像是大楼的保安,它:

  • 将内部设备的私有IP地址转换为公共IP地址
  • 记录内部设备发起的连接,并管理返回的数据包
  • 通常拒绝外部设备直接发起的连接请求

2、STUN服务器的作用

STUN(Session Traversal Utilities for NAT)服务器是一个公网上的"镜子",它告诉你:

  • 你的设备在公网上的IP地址和端口号
  • 你的NAT类型和行为特征

3、UDP打洞的核心思想

  1. 发现阶段:双方都向STUN服务器查询自己的公网地址
  2. 信息交换:通过服务器交换各自的公网地址信息
  3. 同时连接:双方几乎同时向对方的公网地址发送UDP包
  4. 建立通道:NAT设备会认为这些包是之前查询的回复,允许通过

三、NAT类型对比详解

NAT类型开放程度P2P打洞难度通俗解释
Open Internet无NAT直接连接住在街边独栋,门牌号公开
Full Cone NAT完全开放容易公寓保安记住你的脸,任何人都能进来找你
Restricted Cone NAT中等中等保安只允许你联系过的人进来找你
Port Restricted Cone NAT较严格较难保安不仅看脸,还要看具体的"敲门方式"
Symmetric NAT最严格最难每次联系不同的人,保安都给不同的门禁卡,无法预测

四、测试结果分析

为什么有些组合能成功,有些不能?

  • 二边都是Restricted NAT:可以打通

    • 因为双方都先"认识"了对方(通过STUN服务器间接接触)
    • NAT设备会允许来自"认识的人"的连接
  • 任意一边是Symmetric NAT:不能打通

    • Symmetric NAT每次对外连接都用不同的端口
    • 对方无法预测你会用哪个端口来接收数据
    • 就像每次用不同的电话号码打电话,对方无法回拨

五、详细操作步骤

1、环境准备

首先确保你的Python环境已就绪:

# 检查Python版本
python3 --version# 安装必要的库
pip3 install pystun3

2、代码

cat > hole_punching_utils.py << 'EOF'
import threading
import socket
import random
import select
import time
import socket
import binascii
import stun
import socket
import struct
import randomdef is_udp_port_free(host='localhost', port=0):"""检查UDP端口是否空闲"""try:with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:s.bind((host, port))return Trueexcept OSError:return Falsedef get_reliable_free_udp_port(host='localhost', min_port=54320, max_port=65535):"""可靠地获取空闲UDP端口"""max_attempts = 50for attempt in range(max_attempts):port = random.randint(min_port, max_port)if is_udp_port_free(host, port):# 双重检查if is_udp_port_free(host, port):return port# 如果随机选择失败,让系统分配return get_free_udp_port()def _receiver_loop(local_socket):"""接收循环 - 处理所有传入数据"""while True:try:ready = select.select([local_socket], [], [], 1.0)if ready[0]:data, addr = local_socket.recvfrom(65536)print(addr,data.decode('utf-8'))breakexcept BlockingIOError:continueexcept OSError as e:print(f"接收数据时发生OS错误: {e}")breakexcept Exception as e:print(f"接收数据时发生未知错误: {e}")print("接收循环结束")def main():# 1. 准备通信工具(创建UDP socket)source_ip="0.0.0.0"source_port=get_reliable_free_udp_port(source_ip)# 2. 准备多个STUN服务器(备用镜子)stun_hosts=['stun.miwifi.com','stun.easyvoip.com','stun.voipbuster.com']stun_port=3478  # STUN标准端口# 3. 创建并配置UDP sockets = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)s.settimeout(2)s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)s.bind((source_ip, source_port))# 4. 查询NAT类型 - 了解自己的"公寓保安"类型find=Falsefor stun_host in stun_hosts:nat_type, nat = stun.get_nat_type(s, source_ip, source_port,stun_host=stun_host, stun_port=stun_port)print(stun_host,nat_type,nat)if nat_type in ['Restric NAT']:break# 5. 获取公网地址信息 - 了解自己的"窗户位置"external_ip = nat['ExternalIP']external_port = nat['ExternalPort']# 6. 启动接收线程 - 准备听对方喊话thread = threading.Thread(target=_receiver_loop, args=(s,))thread.daemon = Truethread.start()# 7. 显示并交换地址信息print(f"你的公网地址: {external_ip}:{external_port}")print("请将这个地址告诉对方,同时获取对方的公网地址")# 8. 获取对方地址try:input_str = input("请输入对方公网IP:端口 (格式: 192.168.1.1:12345): ").strip().split(":")target_ip = input_str[0]target_port = int(input_str[1])  target = (target_ip, target_port)print(f"🎯 目标地址: {target}")except:print("❌ 输入格式错误,请按 IP:端口 格式输入")return# 9. 开始打洞 - 双方同时"向对方窗户喊话"input("按回车键开始打洞(确保对方也准备好了)...")hostname = socket.gethostname()seq_num=0print("🚀 开始发送数据包...")try:while True:message = f"{hostname}:消息#{seq_num}"s.sendto(message.encode('utf-8'), target)print(f"📤 发送: {message}")time.sleep(1)  # 每秒发送一条消息seq_num += 1except KeyboardInterrupt:print("\n⏹️ 用户停止程序")
if __name__ == "__main__":main()
EOF
python3 hole_punching_utils.py

3、完整操作流程

3.1、在两台设备上运行脚本
# 在设备A上运行
python3 hole_punching_utils.py# 在设备B上运行(同时或稍后)
python3 hole_punching_utils.py
3.2、交换公网地址信息

设备A显示:

🎯 你的公网地址: 123.45.67.89:54321
请将这个地址告诉对方,同时获取对方的公网地址

设备B显示:

🎯 你的公网地址: 987.65.43.21:12345  
请将这个地址告诉对方,同时获取对方的公网地址

交换方式:

  • 设备A输入设备B的地址:987.65.43.21:12345
  • 设备B输入设备A的地址:123.45.67.89:54321
3.3、同步开始打洞

关键要点:

  1. 双方都要在准备好后按回车
  2. 时间间隔尽量短,最好在10秒内完成
  3. 如果第一次失败,可以多试几次
3.4、验证连接

成功迹象:

📤 发送: DeviceA:消息#0
📤 发送: DeviceA:消息#1
('987.65.43.21', 12345) DeviceB:消息#0  # 收到对方消息!
📤 发送: DeviceA:消息#2

六、实际应用场景

UDP打洞技术被广泛应用于:

  • 视频会议系统:如Zoom、Teams的P2P模式
  • 在线游戏:玩家间的直接连接
  • 文件共享:BitTorrent等P2P下载
  • 物联网设备:设备间的直接通信

七、总结

UDP打洞就像是通过一个公共介绍人让两个躲在门后的人建立直接联系。虽然技术原理复杂,但通过STUN服务器的帮助,我们能够巧妙地利用NAT设备的行为特性实现直接通信。

记住成功的关键因素:

  1. ✅ 合适的NAT类型(非对称NAT)
  2. ✅ 准确的公网地址信息
  3. ✅ 双方几乎同时发起连接
  4. ✅ 稳定的网络环境
http://www.dtcms.com/a/482472.html

相关文章:

  • 手机怎样设计网站建设哪个网站有做兼职的
  • 分布式专题——44 ElasticSearch安装
  • Java HTTP编程深度解析:从基础到微服务通信的完整架构实践
  • 3dgs train.py详解
  • Ruby Socket 编程
  • 阿里云linux主机如何添加2个网站中山网站建设方案托管
  • React 状态管理中的循环更新陷阱与解决方案
  • 手机h5免费模板网站深圳网页设计培训要多久
  • 网站快速建设网络营销公司介绍
  • 唐山seo网站建设企业网站的建立如何带来询盘
  • 上海虹口网站建设重庆网站建设公司的网站
  • 自动化测试之 Cucumber 工具
  • 基于MATLAB的t-SNE算法多合成数据集降维可视化实现
  • SAP 关于工单的状态更改,这个要怎么查看呢?
  • 网站建设费用会计分录男女做暧暧视频免费网站
  • 如何高效编写MySQL数据导出与导入语句?
  • 第六部分:VTK进阶(第160章 体绘制采样与空域加速)
  • 网站开发什么意思泾阳做网站
  • 什么是swc?
  • 第九章 装饰器与闭包
  • 接口测试案例从哪些维度去设计
  • 协程入门(基础篇)
  • 建设好网站的在线沟通功能广州开发区投资集团有限公司招聘
  • 如何将 iPhone 联系人同步到 Mac
  • 织梦的网站收录不好保定网站建设设计
  • 网络安全之揭秘APT Discord C2 以及如何取证
  • 第五章 神经网络的优化
  • 网络安全主动防御技术与应用
  • 5. 神经网络的学习
  • 响应式网站页面设计怎么写网站建设推广