打洞原理及应用

什么是打洞?
    
    在传统的集中式网络中,都是一台服务器(集群)对外提供服务,所有客户端都依赖中央服务器进行与服务端的通信或者其他客户端的通信。如图 
 
 
  这样的通信方式,一个数据包从一个客户端发送到另一个客户端都要在服务端中进行中转,服务端承受的压力非常大,很容易因为线程问题而当机。 于是提出另一种通信方式:P2P通信(peer to peer) 对等通信。 即:在p2p的网络中,所有网络节点都是同等地位,没有服务端和客户端之分,一个节点即是服务端也是客户端 例如:BT下载工具,在下载的同时,同时也为其他节点提供下载 通信模式如图:
  即,客户端之间可以进行直接的通信,不需要在经过服务端的中转,从而提高网络传输速度和减小服务器压力,这是非常有用的。P2P虽然通信模式非常理想,但是有一些问题需要解决 1.客户端通信之前,必须知晓接受端的公网IP和端口port 2.客户端的p2p通信数据包必须能够穿透NAT(network address translate) 网络地址翻译 解决方案 1.对于第一个问题比较简单,可以通过一台拥有公网IP的节点来记录在线客户端的公网IP和端口,所有客户端可以通过该节点读取接受客户端的IP和port 对于第二个问题比较复杂,主要针对私有网络之间的通信,由于ip的匮乏,所以网络上不可能所有节点都位于同一个网段(即拥有公网IP),事实上,大部分的节点都处于常规网络的边缘,甚至在DNS所能查询的范围之外,所以在处于网络的边缘的节点不能直接通信的(因为NAT的缘故),如图
  为了简单起见,我们没有画交换机,正常的公司网络都有交换机。 A公司的出口路由IP为211.10.1.1(公网IP),公司有2台主机ip分别为192.168.1.1和192.168.1.2 B公司的出口路由IP为211.10.1.2(公网IP),公司有2台主机ip分别为172.21.1.1和172.21.1.2 那么A公司的192.168.1.1主机与B公司的172.21.1.1在不同的网段,所以不能直接通信。 而p2p的出现,正是要解决这种问题,让两个在不同网段的主机可以直接通信! 在介绍通信之前,还要介绍一个p2p通信的核心,网络打洞(UDP)!
  正如上图所列出的通信模式中,采用upd通信 公网主机211.10.1.2开启一个udp,端口为20000 A公司主机192.168.1.2同样开启一个udp,端口为10000 192.168.1.2发送一个数据包到211.12.1.2,数据报文包含 发送方:192.168.1.2:10000 接受方:211.10.1.2:20000 当数据包经过A公司的出口路由时,路由上的NAT会修改数据包为 发送方:211.10.1.1:随机端口(假设是30000) 接受方:211.10.1.2:20000 并在NAT上创建一个Session,该session包含192.168.1.2:10000 → 211.10.1.2:30000的关联。 即:在NAT上有了192.168.1.2:10000与211.10.1.2:30000的会话 所以在Server中收到的数据包,他看到的发送方是211.10.1.1:30000,这也就是192.168.1.2:10000的公网ip 而Server发送的udp数据包,指定接受方为211.10.1.1:30000,因为在192.168.1.2有一个192.168.1.2:10000->211.10.1.1:30000的会话,所以数据包能成功传递到192.168.1.2 所以在这里的session就是所说的在路由上打的洞(通信通道)。
如果没有session的存在,那么路由接受到的数据包就是不经过认证的,一般的路由会做丢弃的操作!!!
 但是这个session是有时效性的。具体的有效时间可以在路由上设定,但是最小值大概在100秒左右。 所以为了保证能够正常通信,必须让这个session持续在路由器上存在,所以,192.168.1.2就必须定时与Server做心跳,通过心跳来保证session不失效!    
 
 
 下面通过一个p2p及时通信软件来说明  有3个角色:2个client,1个server 2个client都处于网络的边缘(企业局域网内部) server处于公网,拥有公网IP  如下图: 
 
 
  在图中,server的作用只有一个,就是记录所有客户端的公网IP client1登录后,链接server,server就能知道client1的公网IP,并记录 client1并从server读取client2的公网IP client2也做与client1同样的操作 这样clieint1和client2就都知道对方的公网IP 接下来进行网络打洞 为了保证client1的路由器有与client2的session,client1要定时与client2做心跳(可以发送一个空的数据包) 同样,client2也要定时与client1做心跳 这样,双方的通信通道都是通的,就可以进行任意的通信了。
P2P通信就建立了
 
为什么打洞基于UDP协议?
    为什么网上讲到的P2P打洞基本上都是基于UDP协议的打洞?难道TCP不可能打洞?还是TCP打洞难于实现?假设现在有内网客户端A和内网客户端B,有公网服务端S。如果A和B想要进行UDP通信,则必须穿透双方的NAT路由。假设为NAT-A和NAT-B。
    A发送数据包到公网S,B发送数据包到公网S,则S分别得到了A和B的公网IP,S也和A B 分别建立了会话,由S发到NAT-A的数据包会被NAT-A直接转发给A,由S发到NAT-B的数据包会被NAT-B直接转发给B,除了S发出的数据包之外的则会被丢弃。所以:现在A B 都能分别和S进行全双工通讯了,但是A B之间还不能直接通讯。
    解决办法是:A向B的公网IP发送一个数据包,则NAT-A能接收来自NAT-B的数据包并转发给A了(即B现在能访问A了);再由S命令B向A的公网IP发送一个数据包,则NAT-B能接收来自NAT-A的数据包并转发给B了(即A现在能访问B了)。以上就是“打洞”的原理。
但是TCP和UDP在打洞上却有点不同。这是因为伯克利socket(标准socket规范)的API造成的。UDP的socket允许多个socket绑定到同一个本地端口,而TCP的socket则不允许这是这样一个意思:A B要连接到S,肯定首先A B双方都会在本地创建一个socket,去连接S上的socket。创建一个socket必然会绑定一个本地端口(就算应用程序里面没写端口,实际上也是绑定了的,至少Java确实如此),假设为8888,这样A和B才分别建立了到S的通信信道。接下来就需要打洞了,打洞则需要A和B分别发送数据包到对方的公网IP。但是问题就在这里:因为NAT设备是根据端口号来确定session,如果是UDP的socket,A B可以分别再创建socket,然后将socket绑定到8888,这样打洞就成功了。但是如果是TCP的socket,则不能再创建socket并绑定到8888了,这样打洞就无法成功。
 

                
  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值