根据通信双方所处网络环境不同,点对点通信可以划分成以下三类:
i> 公网:公网
ii>公网:内网
iii>内网:内网
前两种容易实现,我们这里主要讨论第三种。
这其中会涉及到NAT和NAPT的概念,请大家预先浏览一下:https://en.wikipedia.org/wiki/Network_address_translation(NAT)。NAT目前主要依托于路由器实现:
其中cone NAT中分三种,它们在内外网ip:port的映射机制也不同:
内网 ip_i:port_i <--> NAT ip_o:port_o (相同的 内网 ip_i:port_i 无论访问哪个外部网址,在映射的生命周期内 NAT ip_o:port_o 不会改变)
Full: 所有发送到ip_o:port_o的消息都会转发到ip_i:port_i上
Restricted: 只有ip_i:port_i 访问过的 addr(不限制端口{host:any}),其发送到ip_o:port_o的消息才会转发到ip_i:port_i上
Port Restricted:只有ip_i:port_i 访问过的 addr(限制端口{host: port_o}),其发送到ip_o:port_o的消息才会转发到ip_i:port_i上
Sysmmetric NAT则与以上都不同:
每次内网ip_i:port_i 访问不同的addr(host不同或port不同,都算是不同地址)时,会映射到不同的 NAT ip_o:port_o
以上四种 NAPT 类型在p2p情况下(A and B)有 10 种组合方式,根据发起方不同可以扩展到 16种情况,这十六种情况中并不是每一种情况都能打洞成功的。现有网上的资料显示,内网:内网 情况下打洞都是需要打洞服务器进行引导的,打洞又分UDP和TCP两种情况,我们大致说一下打洞的流程:
假设 A 和 B 双方都是 cone 模式, 在 服务器 S 的引导下进行 TCP打洞
(1)A 和 B 向 S 通过 UDP 发送消息(S 可以从 UDP 消息提取出其公网 IP)
(2)A (ip_a_i, port_a_i) 向 S 建立 TCP 链接,发送与B 通讯的请求,同时 监听 (ip_a_i, port_a_i)
(3)S 提取出 A tcp 链接的公网地址(ip_a_o, port_a_o), 通过UDP协议向 B 发送(ip_a_o, port_a_o)
(4)B 向 (ip_a_o, port_a_o)发起TCP链接请求,如果失败则执行第5步,如果成功则到此为止
(5)B 保持内网地址(ip_b_i,port_b_i)不变向 S 发起TCP链接请求,通知其已经向A发送了链接请求,并监听(ip_b_i,port_b_i)
(6)S 提取出 B的tcp链接公网(ip_b_o, port_b_o), 通过UDP 将(ip_b_o, port_b_o)发送给A
(7) A 通过(ip_a_i, port_a_i)向 B发起链接请求
其中Sysmmetric :Sysmmetric 和 Sysmmetric:Port Restricted 这两种组合是没办法打洞的:
我们假设A是 Sysmmetric \ B 是 Port Restricted
《1》当 A 向 S 发起tcp链接的时候 假设形成 NAT 映射(192.168.1.4:8080 <--> 61.22.37.16:33956)
《2》S 将 61.22.37.16:33956 转发给 B ,B 向该地址发起链接请求
《3》B的路由器的路由表中会添加 61.22.37.16:33956 这个地址,如果A再次通过该地址来向B发起通信就可以完成通信
然而,当A向B发起访问的时候会形成一个新的 NAT 映射关系,产生一个新的公网地址,所以B不会接受其访问。反之如果A是 Port Restricted\ B是 Sysmmetric 也无法完成打洞,大家可以自己思考一下为什么。