P2P之UDP穿透NAT(重发)

shootingstars 2004-06-14 05:22:58
加精
这篇文章我原来已经发过了,不过后来由于CSDN的故障,贴子丢失了。这次应斑竹PiggyXP的要求,我再发一次。
上次发完贴子后,有很多网友给出了很好的意见,可惜的是,贴子丢失了,我没能记住他们的名字,在这里向这些网友表示感谢。

P2P 之 UDP穿透NAT的原理与实现(附源代码)
原创:shootingstars
参考:http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt

论坛上经常有对P2P原理的讨论,但是讨论归讨论,很少有实质的东西产生(源代码)。呵呵,在这里我就用自己实现的一个源代码来说明UDP穿越NAT的原理。

首先先介绍一些基本概念:
NAT(Network Address Translators),网络地址转换:网络地址转换是在IP地址日益缺乏的情况下产生的,它的主要目的就是为了能够地址重用。NAT分为两大类,基本的NAT和NAPT(Network Address/Port Translator)。
最开始NAT是运行在路由器上的一个功能模块。

最先提出的是基本的NAT,它的产生基于如下事实:一个私有网络(域)中的节点中只有很少的节点需要与外网连接(呵呵,这是在上世纪90年代中期提出的)。那么这个子网中其实只有少数的节点需要全球唯一的IP地址,其他的节点的IP地址应该是可以重用的。
因此,基本的NAT实现的功能很简单,在子网内使用一个保留的IP子网段,这些IP对外是不可见的。子网内只有少数一些IP地址可以对应到真正全球唯一的IP地址。如果这些节点需要访问外部网络,那么基本NAT就负责将这个节点的子网内IP转化为一个全球唯一的IP然后发送出去。(基本的NAT会改变IP包中的原IP地址,但是不会改变IP包中的端口)
关于基本的NAT可以参看RFC 1631

另外一种NAT叫做NAPT,从名称上我们也可以看得出,NAPT不但会改变经过这个NAT设备的IP数据报的IP地址,还会改变IP数据报的TCP/UDP端口。基本NAT的设备可能我们见的不多(呵呵,我没有见到过),NAPT才是我们真正讨论的主角。看下图:
Server S1
18.181.0.31:1235
|
^ Session 1 (A-S1) ^ |
| 18.181.0.31:1235 | |
v 155.99.25.11:62000 v |
|
NAT
155.99.25.11
|
^ Session 1 (A-S1) ^ |
| 18.181.0.31:1235 | |
v 10.0.0.1:1234 v |
|
Client A
10.0.0.1:1234
有一个私有网络10.*.*.*,Client A是其中的一台计算机,这个网络的网关(一个NAT设备)的外网IP是155.99.25.11(应该还有一个内网的IP地址,比如10.0.0.10)。如果Client A中的某个进程(这个进程创建了一个UDP Socket,这个Socket绑定1234端口)想访问外网主机18.181.0.31的1235端口,那么当数据包通过NAT时会发生什么事情呢?
首先NAT会改变这个数据包的原IP地址,改为155.99.25.11。接着NAT会为这个传输创建一个Session(Session是一个抽象的概念,如果是TCP,也许Session是由一个SYN包开始,以一个FIN包结束。而UDP呢,以这个IP的这个端口的第一个UDP开始,结束呢,呵呵,也许是几分钟,也许是几小时,这要看具体的实现了)并且给这个Session分配一个端口,比如62000,然后改变这个数据包的源端口为62000。所以本来是(10.0.0.1:1234->18.181.0.31:1235)的数据包到了互联网上变为了(155.99.25.11:62000->18.181.0.31:1235)。
一旦NAT创建了一个Session后,NAT会记住62000端口对应的是10.0.0.1的1234端口,以后从18.181.0.31发送到62000端口的数据会被NAT自动的转发到10.0.0.1上。(注意:这里是说18.181.0.31发送到62000端口的数据会被转发,其他的IP发送到这个端口的数据将被NAT抛弃)这样Client A就与Server S1建立以了一个连接。

呵呵,上面的基础知识可能很多人都知道了,那么下面是关键的部分了。
看看下面的情况:
Server S1 Server S2
18.181.0.31:1235 138.76.29.7:1235
| |
| |
+----------------------+----------------------+
|
^ Session 1 (A-S1) ^ | ^ Session 2 (A-S2) ^
| 18.181.0.31:1235 | | | 138.76.29.7:1235 |
v 155.99.25.11:62000 v | v 155.99.25.11:62000 v
|
Cone NAT
155.99.25.11
|
^ Session 1 (A-S1) ^ | ^ Session 2 (A-S2) ^
| 18.181.0.31:1235 | | | 138.76.29.7:1235 |
v 10.0.0.1:1234 v | v 10.0.0.1:1234 v
|
Client A
10.0.0.1:1234
接上面的例子,如果Client A的原来那个Socket(绑定了1234端口的那个UDP Socket)又接着向另外一个Server S2发送了一个UDP包,那么这个UDP包在通过NAT时会怎么样呢?
这时可能会有两种情况发生,一种是NAT再次创建一个Session,并且再次为这个Session分配一个端口号(比如:62001)。另外一种是NAT再次创建一个Session,但是不会新分配一个端口号,而是用原来分配的端口号62000。前一种NAT叫做Symmetric NAT,后一种叫做Cone NAT。我们期望我们的NAT是第二种,呵呵,如果你的NAT刚好是第一种,那么很可能会有很多P2P软件失灵。(特别是如果双方都是Symmetric NAT,或者一方是Symmetric NAT,另一方是Restricted Cone NAT,这种情况下,建立p2p的连接将会比较困难。关于Restricted Cone NAT,请参看http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt)
...全文
1178 49 打赏 收藏 转发到动态 举报
写回复
用AI写文章
49 条回复
切换为时间正序
请发表友善的回复…
发表回复
PiggyXP 2004-06-25
  • 打赏
  • 举报
回复
怎么在列表里死活找不到这个帖子啊,我顶上去看看^_^
terrysun 2004-06-25
  • 打赏
  • 举报
回复
好东东啊
wfq771105 2004-06-19
  • 打赏
  • 举报
回复
太经典的文章了,佩服!
shootingstars 2004-06-18
  • 打赏
  • 举报
回复
To AntingZ(夕惕若)(买了一杆▄︻┳═一)
此种情况下,你需要首先确定双方NAT的类型。你可以使用STUN协议来确定NAT的类型。

如果想用此种方法建立连接,必须双方都是Cone NAT,而且必须有一方不是Restricted Cone NAT。
liuzhijun 2004-06-18
  • 打赏
  • 举报
回复
这样的吗?Win2000是Symmetric NAT类型的吗?
我晕啊!那具体那些网络才是CONE NAT的网络呢?
我们用的是Win2000的共享上网,就是Win2000作为网关!
那如果是这样的话!其不是很多P2P都不能用了啊?
AntingZ 2004-06-18
  • 打赏
  • 举报
回复
to 楼主:
问个问题,假设有下面的情况,只有2个客户端,没有服务器

+----------------------+----------------------+
| |
NAT A (外网IP:202.187.45.3) NAT B (外网IP:187.34.1.56)
| (内网IP:192.168.0.1) | (内网IP:192.168.0.1)
| |
Client A (192.168.0.20:4000) Client B (192.168.0.10:40000)

clientA and clientB 都知道自己的外网IP,然后她们通过打电话(or By Email)告诉对方自己的外网IP,然后
(1)clientA发一个UDP包裹给NAT B (外网IP:187.34.1.56),这样就在Nat A上打了一个洞
(2)clientB发一个UDP包裹给NAT A(外网IP:202.187.45.3),这样就在Nat B上打了一个洞
两边都有洞了,现在ClientA and ClientB 是不是就能通信了?




shootingstars 2004-06-18
  • 打赏
  • 举报
回复
你们是使用的Win2000作为网关吗?

Win2000作为NAT是Symmetric NAT(STUN检测结果)。

还有,你们是使用同一个外网IP出去的吗?
注意:如果两个客户端处于同一个NAT后,将可能不能正常通讯。
liuzhijun 2004-06-18
  • 打赏
  • 举报
回复
还是只有一边行,我晕啊!救命啊!神啊!
shootingstars(有容乃大,无欲则刚) 大哥救命啊!
liuzhijun 2004-06-18
  • 打赏
  • 举报
回复
不是吧!你两边都发送成功了吗?是不是和防火墙有关系啊!
你在程序里面有改过东西吗?我测试双方都不是在一个NAT后面啊
我用了3个宿舍的局域网,1个局域网中的主机做服务器,另外两客户端在另外两个NAT里面!
shootingstars 2004-06-18
  • 打赏
  • 举报
回复
To liuzhijun(云)
呵呵,不过我也是用的Win2000 Pro做的网关,并且试验成功了。。。原因我也不知道为什么。。。(不知道STUN是如何判断的?)

再问一下:你们的测试双方是在一个NAT后吗?(Win2000肯定不支持loopback translation)
liuzhijun 2004-06-17
  • 打赏
  • 举报
回复
我们都是中国电信的用的是VDSL拨号上网!还好我们各个宿舍都有电脑,每个宿舍有一个号
都是一个小局域网!呵呵!所以才有机会可以实验!就是这样的吧!因该是CONE NAT吧!
sncel 2004-06-17
  • 打赏
  • 举报
回复
TCP的NAT我倒是用,UDP没有写过,不过都差不多的。
shootingstars 2004-06-17
  • 打赏
  • 举报
回复
to liuzhijun(云)
我想TCP连接仍然存在需要发送心跳包的问题。(NAT仍然可能会因为超时切断这个连接)

TCP的端口与UDP的端口是没有关系的。我想如果使用TCP连接服务器,而使用UDP来连结其他的客户端,这种设计可能会更加复杂,并且我没有实践过。

如果一个端口消失了(作用时间过了),那么下次进行会话时新生成的端口不会一样。
shootingstars 2004-06-17
  • 打赏
  • 举报
回复
to liuzhijun(云)
你能说明一下双方的NAT的情况吗?(双方的NAT的类型)
liuzhijun 2004-06-17
  • 打赏
  • 举报
回复
大家有实验过这个代码的都来发发言吧!我好象测试了不行啊!只能一边!
PRuby 2004-06-17
  • 打赏
  • 举报
回复
学习,顶
liuzhijun 2004-06-17
  • 打赏
  • 举报
回复
eMule上面有具体讲P2P吗?
liuzhijun 2004-06-17
  • 打赏
  • 举报
回复
to shootingstars(有容乃大,无欲则刚)

哎!遇到问题了!我下载的代码不知为什么只能单边发送,就是后来的那个可以发送
另外一边不能发送啊,总是失败,你有没有看过这个的原代码啊?帮我看看好吗?
sharkhuang 2004-06-17
  • 打赏
  • 举报
回复
怎么解决!nat中外网想主动链接内网的机器?
qrlvls 2004-06-17
  • 打赏
  • 举报
回复
致敬,另外推荐大家看看 eMule 的源码哦
加载更多回复(29)

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧