原文地址:http://www.cppblog.com/converse/archive/2006/09/02/11969.html
关于采用UDP协议进行打洞以进行P2P会话的原理,我本来想写一篇文章作说明,但是现在已经有一篇文章把原理性的东西解释清楚了,我在这里不再作这部分的重复,可以参见这里:
P2P 之 UDP穿透NAT的原理与实现(附源代码)--http://www.cnpaf.net/Class/hack/0512182034513804825.htm
下面解释一下上面的文章中没有提及或者说我觉得比较欠缺的地方.
私有地址/端口和公有地址/端口:我们知道,现在大部分网络采用的都是NAPT(Network Address/Port Translator)了,这个东东的作用是一个对外的对话在经过NAT之后IP地址和端口号都会被改写,在这里把一次会话中客户自己认为在使用的IP地址和端口号成为私有地址/端口,而把经过NAPT之后被改写的IP地址和端口号称为公有地址/端口.或者可以这么理解,私有地址/端口是你家里人对你的昵称而公有地址/端口则是你真正对外公开的名字.如何获得用户的私用地址/端口号,这个很简单了,而要得到公有地址/端口号就要在连接上另一台机器之后由那台机器看到的IP地址和端口号来表示.
如果明白了上面的东西,下面进入我们的代码,在这里解释一下关键部分的实现:
客户端首先得到自己的私有地址/终端,然后向server端发送登陆请求,server端在得到这个请求之后就可以知道这个client端的公有地址/终端,server会为每一个登陆的client保存它们的私有地址/端口和公有地址/端口.
OK,下面开始关键的打洞流程.假设client A要向client B对话,但是A不知道B的地址,即使知道根据NAT的原理这个对话在第一次会被拒绝,因为client B的NAT认为这是一个从没有过的外部发来的请求.这个时候,A如果发现自己没有保存B的地址,或者说发送给B的会话请求失败了,它会要求server端让B向A打一个洞,这个B->A的会话意义在于它使NAT B认为A的地址/端口是可以通过的地址/端口,这样A再向B发送对话的时候就不会再被NAT B拒绝了.打一个比方来说明打洞的过程,A想来B家做客,但是遭到了B的管家NAT B的拒绝,理由是:我从来没有听我家B提过你的名字,这时A找到了A,B都认识的朋友server,要求server给B报一个信,让B去跟管家说A是我的朋友,于是,B跟管家NAT B说,A是我认识的朋友,这样A的访问请求就不会再被管家NAT B所拒绝了.简而言之,UDP打洞就是一个通过server保存下来的地址使得彼此之间能够直接通信的过程,server只管帮助建立连接,在建立间接之后就不再介入了.
好了,原理性的东西解释到这里,附件中有一个完整的P2P演示程序,命令行模式下,包括server端和client端,在运行的时候首先启动server端,然后打开几个client端分别登陆,之后彼此之间就可以相互通信了.程序在本机上测试通过,也就是测试的环境server和client都是一台机器,还没有在不同的机器上测试过,不知道会不会有问题:)
下载地址:
http://www.cppblog.com/Files/converse/P2PDemo.rar
参考资料:
1)P2P 之 UDP穿透NAT的原理与实现(附源代码)-http://www.cnpaf.net/Class/hack/0512182034513804825.htm
2)王艳平<<Windows网络与通信程序设计>>