在网络开发中,常常使用UDP通信进行数据传输。随着路由的普及,NAT在UDP数据包回发形成了障碍。我们知道UDP协议是“无连接”的,既然有回发数据的需求,那么路由器就为了这种需求,开发了UDP映射保持。
当电脑A向电脑B发送UDP数据的时候,电脑B可以直接向电脑A直接回发数据包。拓扑如下:
在同一网段下,通信毫无障碍,可以任意发送数据。
在IPV4资源紧张的大背景下诞生的NAT技术缓解了地址紧张的局面,但是也带来了大量设备不能直接访问公网的问题。如下图所示,电脑获得的为私有地址:
在这种网络环境下,拓扑通常是这样的:
这时候,电脑A就不能愉快的通过TCP/IP协议联系到电脑B了。我们知道同一层网络的同一网段的设备可以相互访问,也就是左边的路由器能看见右边的路由器,处于不同局域网的设备不能互相访问,即电脑A和电脑B处于不同的局域网,所以他们互相都不能直接访问。我们讨论的并不是这一种情况。而是:
这时,通过路由器的NAT/NAPT就可以让数据包发送到B电脑,原理如下:
在链接建立的生命周期中,内网的端口和外网的端口通过路由器建立起了映射,达到地址转换/端口转换的效果。这种映射方式,对于TCP这种有链接的协议来说,很容易确定映射的生命周期。但是对于UDP来说,就很困难了。还好,路由器厂家在做NAT的时候想到了这一点,在UDP从内网发送的时刻开始,为UDP数据回送保留了映射,这个时间根据路由器的厂家不同,保留映射数秒到数分钟不等。这个时间叫做Session(会话)保持时间。
为了验证网络UDP“打洞”是否可行,我们采用以下方法验证数据回发:
1、使用一台局域网内的电脑A和一台具有公网IP地址的电脑B;
2、使用电脑A向电脑B发送一个UDP数据包;
3、在电脑B接受到数据包时,立刻向源地址返回一个数据包。
如果Session会保持,则电脑A会收到电脑B返回的数据包。
为了实现以上效果,作者使用了两段Python程序来做这个实验:
局域网内电脑A的程序,会向电脑B发送“Hello”,然后静等数据返回:
import socket
if __name__ == "__main__":
addr = ('127.0.0.1',21500)
receive = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
receive.bind(addr)
msg = 'Hi!\r\n'
data,addr = receive.recvfrom(2048)
print(addr,' : ',data)
receive.sendto(msg.encode('UTF_8'),addr)
receive.close()
具有公网IP地址的电脑B的程序(IP地址略去),接收到数据后会立刻回发“Hi”:
import socket
if __name__ == "__main__":
addr = ('x.x.x.x',21500)
receive = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
receive.bind(addr)
msg = 'Hi!\r\n'
data,addr = receive.recvfrom(2048)
print(addr,' : ',data)
receive.sendto(msg.encode('UTF_8'),addr)
receive.close()
通常Session保持会持续一段时间,如何测试这段时间有多长呢?我们采用以下方法验证:
1、使用一台局域网内的电脑A和一台具有公网IP地址的电脑B;
2、使用电脑A向电脑B发送一个UDP数据包;
3、在电脑B接受到数据包时,延迟一段时间向源地址返回一个数据包。
4.1、若电脑A收到数据,增大步骤3的延迟时间,重复2。
4.2、若电脑A无法收到数据,实验结束,最后一次成功收到数据的时间为当前网络下的最大保持时间。
电脑A的程序保持不变,电脑B的程序变化如下:
import socket
import time
if __name__ == "__main__":
addr = ('x.x.x.x',21500)
receive = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
receive.bind(addr)
msg = 'Hi!\r\n'
data,addr = receive.recvfrom(2048)
print(addr,' : ',data)
time.sleep(10)#这里为延迟时间
receive.sendto(msg.encode('UTF_8'),addr)
receive.close()
最终作者的网络环境测试的最大延迟时间为5分钟。
通过这个实验,说明网络设备在NAT/NAPT时对UDP通信已经做优化,只要延迟时间不是太长,很大概率可以将回传数据包成功送达。更有厂家对UDP Session保持时间做优化,在端口资源不紧张时长期保持端口映射。