• 网络穿透:TCP 打洞、UDP 打洞与 UPnP


    在现代网络中,很多设备都处于 NAT(网络地址转换)或防火墙后面,这使得直接访问这些设备变得困难。在这种情况下,网络穿透技术就显得非常重要。本文将介绍三种常用的网络穿透技术:TCP 打洞、UDP 打洞和 UPnP。
    在这里插入图片描述

    一、TCP 打洞

    1.1 什么是 TCP 打洞?

    TCP 打洞(TCP Hole Punching)是一种使 NAT 后的两个客户端通过第三方服务器建立直接连接的方法。NAT 通常会阻止外部主机直接与内部主机通信,因此需要借助外部服务器来协调连接。

    1.2 工作原理

    1. 建立与中继服务器的连接:两个 NAT 后的客户端 A 和 B 先分别与公共服务器 S 建立连接。
    2. 交换外部地址:服务器 S 了解 A 和 B 的外部 IP 和端口,并将这些信息发送给彼此。
    3. 尝试直接连接:A 和 B 分别尝试使用彼此的外部 IP 和端口进行连接,如果两端的 NAT 设备允许,则连接成功。

    在这里插入图片描述

    1.3 示例代码

    以下是一个简单的 Python 示例,演示了通过 TCP 打洞进行连接的过程。

    import socket
    
    # Server listens for incoming connections and exchanges client information
    def server():
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind(('0.0.0.0', 12345))
        s.listen(2)
        
        print("Server waiting for connections...")
        conn_a, addr_a = s.accept()
        print(f"Client A connected: {addr_a}")
        
        conn_b, addr_b = s.accept()
        print(f"Client B connected: {addr_b}")
        
        # Exchange addresses
        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()
        s.close()
    
    # Clients attempt to connect to each other using exchanged information
    def client():
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(('server_ip', 12345))  # Replace 'server_ip' with the actual IP of the server
        
        peer_info = s.recv(1024).decode()
        peer_ip, peer_port = peer_info.split(':')
        
        # Attempt to connect to peer
        try:
            peer_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            peer_socket.connect((peer_ip, int(peer_port)))
            print("Connected to peer!")
        except Exception as e:
            print(f"Failed to connect to peer: {e}")
        
        s.close()
    

    二、UDP 打洞

    2.1 什么是 UDP 打洞?

    UDP 打洞(UDP Hole Punching)与 TCP 打洞类似,是一种让处于 NAT 后的两台主机通过第三方服务器建立直接 UDP 连接的技术。与 TCP 不同的是,UDP 是无连接的协议,允许 NAT 主机更容易接受来自外部的连接请求。

    2.2 工作原理

    1. 与服务器通信:两台客户端 A 和 B 分别与公共服务器 S 进行通信,服务器记录它们的外部 IP 和端口。
    2. 交换地址:服务器将 A 和 B 的外部 IP 和端口互相传递。
    3. 直接发送 UDP 数据包:A 和 B 尝试通过彼此的外部地址直接发送 UDP 数据包,利用 NAT 会话表进行数据传输。
      在这里插入图片描述

    2.3 示例代码

    import socket
    
    # UDP Server to exchange addresses
    def udp_server():
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.bind(('0.0.0.0', 12345))
        
        print("Server waiting for messages...")
        data_a, addr_a = s.recvfrom(1024)
        print(f"Received from A: {addr_a}")
        
        data_b, addr_b = s.recvfrom(1024)
        print(f"Received from B: {addr_b}")
        
        # Exchange addresses
        s.sendto(f"{addr_b[0]}:{addr_b[1]}".encode(), addr_a)
        s.sendto(f"{addr_a[0]}:{addr_a[1]}".encode(), addr_b)
    
    # UDP Client to communicate through hole punching
    def udp_client():
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.sendto(b'Hello from client', ('server_ip', 12345))  # Replace 'server_ip' with actual server IP
        
        peer_info, _ = s.recvfrom(1024)
        peer_ip, peer_port = peer_info.decode().split(':')
        
        # Send message to peer
        s.sendto(b'Hello peer!', (peer_ip, int(peer_port)))
        
        try:
            response, _ = s.recvfrom(1024)
            print(f"Received from peer: {response}")
        except socket.timeout:
            print("No response from peer")
        
        s.close()
    

    三、UPnP(通用即插即用)

    3.1 什么是 UPnP?

    UPnP(Universal Plug and Play,通用即插即用)是一种网络协议,允许设备自动发现和与网络中的其他设备进行通信。在 NAT 环境下,UPnP 可以自动打开路由器的端口,从而允许外部设备访问位于内网中的设备。

    UPnP 主要用于家庭网络和小型局域网,它通过设备的自动配置来简化网络中的设备通信过程。

    3.2 工作原理

    1. 设备发现:客户端设备通过发送 SSDP(简单服务发现协议)请求,查找网络中的 UPnP 设备。
    2. 获取路由器的设备描述:通过 SSDP 发现的设备提供一个设备描述 XML 文件,描述其功能和端点。
    3. 请求端口映射:客户端通过向路由器发送请求,要求映射一个外部端口到内网设备的特定端口。
      在这里插入图片描述

    3.3 示例代码

    可以使用第三方库 miniupnpc 来实现 UPnP 端口映射,以下是一个 Python 示例。

    pip install miniupnpc
    
    import miniupnpc
    
    def upnp_port_mapping():
        upnp = miniupnpc.UPnP()
        upnp.discoverdelay = 200
        upnp.discover()  # Discover UPnP devices
        upnp.selectigd()  # Select Internet Gateway Device
        
        external_port = 12345
        internal_port = 54321
        local_ip = upnp.lanaddr  # Get local IP address
        
        # Add port mapping (TCP)
        upnp.addportmapping(external_port, 'TCP', local_ip, internal_port, 'Test Port Mapping', '')
        
        print(f"Port {external_port} mapped to {local_ip}:{internal_port} (TCP)")
        
        # Optionally, remove the port mapping
        # upnp.deleteportmapping(external_port, 'TCP')
        
    upnp_port_mapping()
    

    四、总结

    • TCP 打洞:通过第三方服务器交换外部地址,尝试建立直接的 TCP 连接。
    • UDP 打洞:类似 TCP 打洞,但使用 UDP 协议,更容易成功。
    • UPnP:通过自动化的端口映射,使内网设备更易于被外部设备访问。

    这三种技术在 P2P 应用中非常重要,特别是在 NAT 或防火墙环境下,它们能够显著提高连接的成功率。

  • 相关阅读:
    前端例程20221024:流光效果按钮
    C++多态的理解
    报名开启!飞桨AI for Science公开课与共创计划邀您来学,探索AI与科学的融合
    selenium的使用细节
    计算机毕设(附源码)JAVA-SSM家教中心管理系统
    DeckGL MVTLayer+Mapbox注入shader实现简单效果
    【C++】C++11新特性
    看了我的MyBatis-Plus用法,同事也开始悄悄模仿了.
    Unity 读取DICOM文件,并支持移动端
    开发者,你对云计算可能有些误解
  • 原文地址:https://blog.csdn.net/problc/article/details/142280471