本文来自依云's Blog,转载请注明。
两台没有外网 IP、在 NAT 后边的主机如何直连?UDP打洞通常可行,但是需要第三方服务器。方法如下:
在服务器 S 上监听一个 UDP 端口,在收到 UDP 数据包后把源地址发回去。代码如下(github):
import sys import time import socket def main(port): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(('', port)) try: while True: data, addr = s.recvfrom(4096) back = 'Your address is %r\n' % (addr,) s.sendto(back.encode(), addr) print(time.strftime('%Y-%m-%d %H:%M:%S'), addr, 'just sent us a message:', data.decode('utf-8', 'replace'), end='') except KeyboardInterrupt: print() if __name__ == '__main__': try: main(int(sys.argv[1])) except (ValueError, IndexError): sys.exit('which port to listen?')
主机 A 发送数据包:
$ socat readline udp:xmpp.vim-cn.com:2727,sourceport=4567 my addr? Your address is ('a.b.c.d', 40060)
输入任意消息并回车,一个 UDP 就从本地的 4567 发送出去了。从上述示例我们可以看到,NAT 设备转发时是从 40060 端口发送出去的。为了让服务器返回的数据能够到达内网主机,在一段时间内,NAT 设备会记住外网来自 40060 端口的 UDP 数据包要发送给主机 a.b.c.d 的 4567 端口。完全圆锥型NAT不会在意外部数据包是从什么地方发回来的。受限圆锥型NAT会忽略掉其它主机的数据包,上例中只认可来自 xmpp.vim-cn.com 的数据包。端口受限圆锥型NAT更进一步地要求源端口(上例中是 2727)必须跟之前发出的数据包的目的端口一致。当然,「之前发出的数据包」不必是最后一个。所以,除了最后一种——对称NAT——之外,其它类型的NAT都是有可能成功穿透的。参见维基百科条目网络地址转换和STUN。
后来通过 pystun 程序,我得知我所处的 NAT 是完全圆锥型的。
在知道 A 的发送地址后,主机 B 就可以向这个地址发送数据了。接下来的操作使用 socat 命令就是:
# host A $ socat readline udp-listen:4567 # host B $ socat readline udp:A:4567
然后 B 先发送数据让 A 知道 B 的地址(socat 会 connect 到这个地址),双方就可以相互通信了。当然,因为是 UDP 协议,所以通信是不可靠的,丢包啊乱序啊都有可能。
2013年10月13日更新:想要连接到 NAT 后边的 mosh 请看这里~
Aug 28, 2012 06:47:47 PM
我倒是对 sctp 的打洞比较感兴趣,如果可以把这个扩展到 sctp 协议就好了。
在华为的路由器上查看当前的 nat 映射的命令似乎是 disp nat sessions ,我也记得不是很清楚,反正是类似的命令。
Aug 29, 2012 12:23:04 AM
搜索「SCTP 打洞」,第一个结果是这里……
SCTP 没用过呢,打洞如何进行?
Aug 29, 2012 12:48:02 AM
其实我也不是很了解。 sctp 支持建立连接的双方使用多个可用的 ip ,我不知道可否使用某些 ip 来建立连接,另一些 ip 来传输数据。如果可以自由设定自己的 ip 的话,应该就可以做到和你做的这个类似的效果,换句话说,就是找个“搭桥”的服务器来打洞。
Oct 25, 2012 11:33:43 AM
学习一下罗~
Jul 31, 2014 10:53:40 PM
我想部署一个rtp服务器(在腾讯云),这个要如何处理?有什么open source介绍吗?
Aug 01, 2014 12:18:33 PM
我不了解 rtp 呢。
Aug 01, 2014 04:58:31 PM
其实这边已经实现了局域网的操作,就是可以把声音传送给局域网的PC,但是如果要在外网操作的话,要如何部署云服务器?这一步我不太懂
Dec 11, 2014 03:35:28 PM
实验成功,两个注意事项:
1. 服务端的python脚本需要python3 环境, 需要到官网下源码安装。
2. socat的readline选项需要安装readline及readline-devel
Dec 11, 2014 05:14:12 PM
呃,是 CentOS 至今源里还没有 Python 3 么……
Dec 11, 2014 05:46:52 PM
CentOS6.5 官方源里没找到。