现在有一段时间,我的UDP打孔系统出现了严重的问题,这可能涉及到我现在无法触及的事情。我会尽可能准确地解释,因为这真的让我感到烦恼:
UDP打孔方法涉及第三个主机S,它基本上是公共的(不在任何类型的NAT或持久防火墙后面),两个客户端A和B将使用它们相互了解。客户端A向S发送udp数据包,以便S可以知道其公共IP和端口(由A&#NAT映射)。客户B也这样做。然后,S通过发送到A B的公共地址和A的公共地址来匹配A和B.因此,他们每个人都知道对方的地址并且可以进行通信。到目前为止一切顺利,但这里有一个问题:它仅适用于特殊情况。
我知道对称NAT(将您的私有IP和端口映射到您想要覆盖的每个目标地址的特定不同地址的NAT)所引起的问题,所以我要说我向S1发送一个数据包, S2,S1将看到具有端口50263的公共地址,而S2将看到50264)。我也知道某些非对称NAT要求您在允许从同一地址接收UDP数据包之前将UDP数据包发送到特定地址。
所以我到目前为止所理解的是,只要你的NAT不是Symmetric,当打开一个新套接字并发送任何字节数组时,你的NAT就会为它分配一个公共IP和端口。公共服务器告诉您的对等方这个公共地址,以便它可以与您通信。然后,您必须向该对等方发送数据包,以便他回复。
但它实际上并不是每次都有效。我已经在我的大学公寓里尝试过,并设法使用我的公共IP地址连接到自己,即使是在NAT后面。我还设法向朋友发送了一个UDP数据包,但它在我做的一百次测试中只运行了一次。我现在正在家里尝试,但它根本不起作用。
我在互联网上设置了2个公共VPS,并向每个人发送了一个数据包以查看我的NAT是否是对称的,但不是,两个端口上显示的端口是相同的。我只能从那些公共服务器上找到自己。使用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()
在我的一个公共VPS上执行,它有效。在家里执行,我没有收到任何东西。似乎从公共主机发送授予所有访问权限,但NAT后面完全不同。
我错过了一些基本的东西吗?我想我几乎每次都做过测试但没有成功。
答案 0 :(得分:0)
根据您要实现的目标,您可能希望使用IGD(spec)来检索您的公共IP和/或自动配置端口转发。据我所知,大多数家用路由器都支持UPnP IGD。
这是一个使用miniupnpc的示例脚本,一个最小的UPnP IGD客户端,以及来自python标准库的ipaddress和socket模块:
[30, 30, 1000]
示例输出:
#!/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:]))