当前位置: 首页 > 知识库问答 >
问题:

UDP穿孔在非对称NAT上失败

微生雨泽
2023-03-14

到目前为止还不错,但这里有一个问题:它只适用于特殊情况。

我知道对称NAT(将您的专用IP和端口映射到您想要到达的每个目的地址的特定不同地址的NAT)引起的问题,因此假设我向S1和S2发送一个数据包,S1将看到端口为50263的公共地址,而S2将看到50264)。我还知道,某些非对称NAT要求您先将UDP数据包发送到特定地址,然后才能从同一地址接收数据包。

所以到目前为止我所理解的是,只要您的NAT不是对称的,当打开一个新套接字并发送任何字节数组时,您的NAT就会为它分配一个公共IP和端口。公共服务器告诉您的对等方这个公共地址,以便它能够与您通信。然后,您必须向该对等方发送一个数据包,以便允许他回复。

但实际上并不是每次都起作用。我试过在我的大学公寓,并设法使用我的公共IP地址连接到我自己,即使是在NAT后面。我还设法将一个UDP数据包发送到一个朋友的,但在我进行的数百次测试中,它只工作了一次。我现在在家里尝试,它根本不起作用。

我在internet上设置了2个公共VP,并向每个VP发送了一个数据包,以查看我的NAT是否对称,但不是,显示在两个VP上的端口是相同的。我只能从那些公共服务器上找到我自己。使用Wireshark,我观察数据包离开我的计算机,但似乎它们在返回之前就被丢弃了。

我编写了简单的python脚本来尝试简单地发送给自己:

服务器

import socket
import sys
from helper import *

# This script is used to allow a client to know which external address it has.

def main():

    # Port used to receive client requests
    port = 6666

    # We create a UDP socket on this port
    sockfd = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)
    sockfd.bind (("", port))

    print ("Address discoverer started on port %d" % port)

    # Server loop
    while True:

        # We wait for any client to request for his address.
        data, addr = sockfd.recvfrom(1)

        # Once a client has sent a request, we send him back his external address, which we obtained using the packet he sent us.
        sockfd.sendto (addr2bytes(addr), addr)

        print ("Client %s:%d requested his address" % addr)

if __name__ == "__main__":
    main()
import sys
import socket
from helper import *

def main():

    master = ("xxx.xxx.xxx.xxx", 6666)
    me = ("yyy.yyy.yyy.yyy", 6666)

    sockfd = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)
    sockfd.bind(('',6666))
    sockfd.sendto (bytearray([0]), master)

    data, addr = sockfd.recvfrom (6)
    a = bytes2addr(data)
    print ('My address is %s:%d' % a)

    for i in range (0, 10):

        sockfd.sendto ('hi myself'.encode('utf-8'), me)
        data, addr = sockfd.recvfrom (1024)
        print ("Received : " + data.decode('utf-8'))

if __name__ == "__main__":
    main()

在我的一个公共副总裁身上执行,起作用了。在家里执行,我什么也没收到。从公共主机发送似乎授予所有访问权限,但在NAT背后则完全不同。

我是不是错过了什么基本的东西?我想我几乎做了所有可能的测试,但没有成功。

共有1个答案

闾丘鸣
2023-03-14

根据您想要实现的目标,您可能希望使用IGD(spec)检索您的公共IP和/或自动配置端口转发。据我所知,大多数家用路由器都支持UPnP IGD。

下面是一个示例脚本,它使用miniupnp,一个最小的UPnP IGD客户端,以及来自Python标准库的ipaddress和socket模块:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
    IGDTestScript.py
    ================

    Description:           UPnP IGD example script
    Author:                shaggyrogers
    Creation Date:         2018-03-28
    License:               None / Public domain
"""

import ipaddress
import socket
import sys

import miniupnpc


def main(args):
    # Get IP address for default interface
    allhosts = ipaddress.ip_address(socket.INADDR_ALLHOSTS_GROUP)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.connect((allhosts.compressed, 0))
    localIP = ipaddress.ip_address(sock.getsockname()[0])

    if localIP.is_global:
        print(f'Public IP: {localIP} (no NAT)')
        return 0

    print(f'Local IP: {localIP}')

    # Discover and connect to IGD device
    igd = miniupnpc.UPnP()
    assert igd.discover() > 0
    assert igd.selectigd() != ''

    # Get public IP
    publicIP = ipaddress.ip_address(igd.externalipaddress())
    assert publicIP.is_global
    print(f'Public IP: {publicIP}')

    # Dump port mappings
    i, mapping = 0, igd.getgenericportmapping(0)

    while mapping:
        print(f'Port mapping {i}: {mapping}')
        i += 1
        mapping = igd.getgenericportmapping(i)

    # Create/update port mapping
    externalPort, internalPort, protocol = 1234, 4321, 'TCP'
    assert igd.addportmapping(externalPort, protocol, localIP.compressed,
                            internalPort, 'test mapping', '')
    print((f'Added port mapping: {protocol} {publicIP}:{externalPort}'
           f' --> {localIP.compressed}:{internalPort}'))

    return 0


if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))

示例输出:

Local IP: 192.168.1.123
Public IP: 203.0.113.1
Port mapping 0: (8908, 'TCP', ('192.168.1.123', 9100), 'tcp_map', '1', '', 0)
Port mapping 1: (22222, 'UDP', ('192.168.1.123', 22222), 'udp_map', '1', '', 0)
Added port mapping: TCP 203.0.113.1:1234 -> 192.168.1.123:4321
 类似资料:
  • 我正在尝试用打孔实现P2P。流程如下: 两个对等体(P1、P2)将向服务器发送1个数据包。 服务器会对两者进行回复,告诉其他服务器IP:端口 P1和P2接收此UDP数据包,知道对方的外部/公共IP:端口。 P1、P2开始向其他对等方公共IP:端口发送数据包。 一旦对等方接收到其他对等方的数据包,我假设孔已被打孔,并将此套接字提供给我的应用程序。 我在不同的路由器上进行了测试,结果如下: 当P1和P

  • 问题内容: 我试图在Java中实现UDP-Holepunching的简单草图以测试其概念,并稍后在我的C / C ++应用程序中使用它。 从Wikipedia来说,我的理解是:A和B是未定义网络结构背后的客户端,C是著名的公共可访问服务器。 A将数据包发送到服务器C,服务器保存其IP地址和端口。C将获得A的NAT的公共IP地址。这样做,A前面的NAT将创建一条路由,该路由会将此端口上的所有数据包传

  • 我知道这个话题并不新鲜。虽然存在各种信息,但健壮的解决方案并没有出现(至少我没有找到)。我有一个用python3编写的P2P守护进程,pie上的最后一个元素是通过TCP连接NAT后面的两个客户端。我得此主题得参考资料: https://bford.info/pub/net/P2pnat/ 如何使两个客户机在连接了一个汇点服务器后直接连接? TCP穿孔问题 到目前为止我所做的: 服务器: 现在,当前

  • 我试图在一个软件中实现一个打孔功能。问题是,我用一个已经制作好的TCP服务器来实现它,以便与用户通信。 以下是我目前掌握的情况: “a”向UDP服务器“US”(端口9333上)发送消息 “用户”将其连接的端口(端口31000-本地端口31005)发送回“A” “a”向TCP服务器“TS”发送消息,表示他想要连接到B(并提供端口31000) “ts”向“b”发送消息,将“a”的端口(31000)和I

  • 我正在尝试用Boost::ASIO实现NAT打孔。根据我的理解,NAT穿孔器的工作原理是这样的(UDP/TCP): 客户端A绑定到端口并连接到服务器S,客户端B执行相同操作。 当S同时接收到请求和匹配时,它将A的ip和端口发送给B,B发送给A。 a和B接收对方的ip和端口,现在它们从同一端口向对方发送消息并形成连接(因为它们正在等待回复?) 如果没有成功的或,我似乎无法运行任何。当然,当目标端口没

  • 大多数人似乎都通过UDP使用NAT穿孔,但在我的例子中,我需要使用TCP(这是事实,在有人问“为什么不使用UDP?”之前)我找不到任何示例代码或至少一个适当的教程。我能找到的都是理论上的附属物,但没有使用套接字或TCPClients(我更喜欢套接字)。 有人能给我链接一些示例代码吗?我可以用这些示例代码连接两个NAT后的客户端,或者用示例代码链接到完整的教程?我知道在.NET中很难实现这个目标(我