问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

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

创作时间:
作者:
@小白创作中心

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

引用
1
来源
1.
https://cloud.tencent.com/developer/article/2457072

在网络通信中,NAT(网络地址转换)和防火墙常常成为设备间直接通信的障碍。本文将介绍三种常用的网络穿透技术:TCP打洞、UDP打洞和UPnP,帮助读者理解如何在NAT环境下实现设备间的直接通信。

一、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或防火墙环境下,它们能够显著提高连接的成功率。

本文参与腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。

原始发表:2024-09-15,如有侵权请联系cloudcommunity@tencent.com删除

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号