TCP打洞技术
//转http://iamgyg.blog.163.com/blog/static/3822325720118202419740/
建立穿越NAT设备的p2p的TCP连接仅仅比UDP复杂一点点,TCP协议的“打洞”从协议层来看是与UDP
的“打洞”过程非常相似的。虽然如此,基于TCP协议的打洞至今为止还没有被非常好的理解,这也
造成了对其提供支持的NAT设备不是非常多。在NAT设备支持的前提下,基于TCP的“打洞”技术实际上
与基于UDP的“打洞”技术一样快捷、可靠。实际上,仅仅要NAT设备支持的话,基于TCP的p2p技术
的健壮性将比基于UDP的技术的更强一些,由于TCP协议的状态机给出了一种标准的方法来精确的
获取某个TCP session的生命期,而UDP协议则无法做到这一点。
4.1 套接字和TCPport的重用
实现基于TCP协议的p2p“打洞”过程中,最基本的问题不是来自于TCP协议,而是来自于来自于应用
程序的API接口。这是因为标准的伯克利(Berkeley)套接字的API是环绕着构建client/server程序
而设计的,API同意TCP流套接字通过调用connect()函数来建立向外的连接,或者通过listen()和
accept函数接受来自外部的连接,可是,API不提供类似UDP那样的,同一个port既能够向外连接,
又可以接受来自外部的连接。并且更糟的是,TCP的套接字通常仅同意建立1对1的响应,即应用程
序在将一个套接字绑定到本地的一个port以后,不论什么试图将第二个套接字绑定到该port的操作都会
失败。
为了让TCP“打洞”可以顺利工作,我们须要使用一个本地的TCPport来监听来自外部的TCP连接,同一时候
建立多个向外的TCP连接。幸运的是,全部的主流操作系统都可以支持特殊的TCP套接字參数,通常
叫做“SO_REUSEADDR”,该參数同意应用程序将多个套接字绑定到本地的一个endpoint(仅仅要全部要
绑定的套接字都设置了SO_REUSEADDR參数就可以)。BSD系统引入了SO_REUSEPORT參数,该參数用于区分
port重用还是地址重用,在这种系统里面,上述全部的參数必须都设置才行。
4.2 打开p2p的TCP流
假定clientA希望建立与B的TCP连接。我们像通常一样假定A和B已经与公网上的已知serverS建立了TCP
连接。server记录下来每一个联入的client的公网和内网的endpoints,如同为UDP服务的时候一样。
从协议层来看,TCP“打洞”与UDP“打洞”是差点儿全然同样的过程。
1、clientA使用其与serverS的连接向server发送请求,要求serverS协助其连接clientB。
2、S将B的公网和内网的TCP endpoint返回给A,同一时候,S将A的公网和内网的endpoint发送给B。
3、clientA和B使用连接S的port异步地发起向对方的公网、内网endpoint的TCP连接,同一时候监听
各自的本地TCPport是否有外部的连接联入。
4、A和B開始等待向外的连接是否成功,检查是否有新连接联入。假设向外的连接因为某种网络
错误而失败,如:“连接被重置”或者“节点无法訪问”,client仅仅须要延迟一小段时间(比如
延迟一秒钟),然后又一次发起连接就可以,延迟的时间和反复连接的次数能够由应用程序编写者
来确定。
5、TCP连接建立起来以后,client之间应该開始鉴权操作,确保眼下联入的连接就是所希望的
连接。假设鉴权失败,client将关闭连接,而且继续等待新的连接联入。client通常採用
“先入为主”的策略,仅仅接受第一个通过鉴权操作的client,然后将进入p2p通信过程不再继续
等待是否有新的连接联入。

(图 7)
与UDP不同的是,使用UDP协议的每一个client仅仅须要一个套接字就可以完毕与serverS通信,
并同一时候与多个p2pclient通信的任务,而TCPclient必须处理多个套接字绑定到同一个本地
TCPport的问题,如图7所看到的。
如今来看更加实际的一种情景,A与B分别位于不同的NAT设备后面,如图5所看到的,而且假定图中
的port号是TCP协议的port号,而不是UDP的port号。图中向外的连接代表A和B向对方的内网
endpoint发起的连接,这些连接也许会失败或者无法连接到对方。如同使用UDP协议进行“打洞”
操作遇到的问题一样,TCP的“打洞”操作也会遇到内网的IP与“伪”公网IP反复造成连接失败或者
错误连接之类的问题。
client向彼此公网endpoint发起连接的操作,会使得各自的NAT设备打开新的“洞”同意A与B的
TCP数据通过。假设NAT设备支持TCP“打洞”操作的话,一个在client之间的基于TCP协议的流
通道就会自己主动建立起来。假设A向B发送的第一个SYN包发到了B的NAT设备,而B在此前没有向
A发送SYN包,B的NAT设备会丢弃这个包,这会引起A的“连接失败”或“无法连接”问题。而此时,
因为A已经向B发送过SYN包,B发往A的SYN包将被看作是由A发往B的包的回应的一部分,
所以B发往A的SYN包会顺利地通过A的NAT设备,到达A,从而建立起A与B的p2p连接。
4.3 从应用程序的角度来看TCP“打洞”
从应用程序的角度来看,在进行TCP“打洞”的时候都发生了什么呢?假定A首先向B发出SYN包,
该包发往B的公网endpoint,而且被B的NAT设备丢弃,可是B发往A的公网endpoint的SYN包则
通过A的NAT到达了A,然后,会发生下面的两种结果中的一种,详细是哪一种取决于操作系统
对TCP协议的实现:
(1)A的TCP实现会发现收到的SYN包就是其发起连接并希望联入的B的SYN包,通俗一点来说
就是“说曹操,曹操到”的意思,本来A要去找B,结果B自己找上门来了。A的TCP协议栈因此
会把B做为A向B发起连接connect的一部分,并觉得连接已经成功。程序A调用的异步connect()
函数将成功返回,A的listen()等待从外部联入的函数将没有不论什么反映。此时,B联入A的操作
在A程序的内部被理解为A联入B连接成功,而且A開始使用这个连接与B開始p2p通信。
因为收到的SYN包中不包括A须要的ACK数据,因此,A的TCP将用SYN-ACK包回应B的公网endpoint,
而且将使用先前A发向B的SYN包一样的序列号。一旦B的TCP收到由A发来的SYN-ACK包,则把自己
的ACK包发给A,然后两端建立起TCP连接。简单的说,第一种,就是即使A发往B的SYN包被B的NAT
丢弃了,可是因为B发往A的包到达了A。结果是,A觉得自己连接成功了,B也觉得自己连接成功
了,无论是谁成功了,总之连接是已经建立起来了。
(2)第二种结果是,A的TCP实现没有像(1)中所讲的那么“智能”,它没有发现如今联入的B
就是自己希望联入的。就好比在机场接人,明明遇到了自己想要接的人却不认识,误觉得是其他
的人,安排别人给接走了,后来才知道是自己错过了机会,可是不管怎样,人已经接到了任务
已经完毕了。然后,A通过常规的listen()函数和accept()函数得到与B的连接,而由A发起的向
B的公网endpoint的连接会以失败告终。虽然A向B的连接失败,A仍然得到了B发起的向A的连接,
等效于A与B之间已经联通,无论中间过程怎样,A与B已经连接起来了,结果是A和B的基于TCP协议
的p2p连接已经建立起来了。
第一种结果适用于基于BSD的操作系统对于TCP的实现,而另外一种结果更加普遍一些,多数linux和
windows系统都会依照另外一种结果来处理。
TCP打洞技术的更多相关文章
- P2P技术基础: 关于TCP打洞技术
4 关于TCP打洞技术 建立穿越NAT设备的p2p的 TCP 连接只比UDP复杂一点点,TCP协议的“打洞”从协议层来看是与UDP的“打洞”过程非常相似的.尽管如此,基于TCP协议的打洞至今为止还没有 ...
- [转]UDP/TCP穿越NAT的P2P通信方法研究(UDP/TCP打洞 Hole Punching)
[转]UDP/TCP穿越NAT的P2P通信方法研究(UDP/TCP打洞 Hole Punching) http://www.360doc.com/content/12/0428/17/6187784 ...
- [p2p]UDP用打洞技术穿透NAT的原理与实现
首先先介绍一些基本概念: NAT(Network Address Translators),网络地址转换:网络地址转换是在IP地址日益缺乏的情况下产生的, ...
- TCP打洞和UDP打洞的区别 (转)
为什么网上讲到的P2P打洞基本上都是基于UDP协议的打洞?难道TCP不可能打洞?还是TCP打洞难于实现? 假设现在有内网客户端A和内网客户端B,有公网服务端S. 如果A和B想要进行UD ...
- p2p 打洞技术
根据通信双方所处网络环境不同,点对点通信可以划分成以下三类:i> 公网:公网ii>公网:内网iii>内网:内网前两种容易实现,我们这里主要讨论第三种.这其中会涉及到NAT和NAPT的 ...
- TCP打洞与UDP打洞的差别
为什么网上讲到的P2P打洞基本上都是基于UDP协议的打洞?难道TCP不可能打洞?还是TCP打洞难于实现? 如果如今有内网clientA和内网clientB.有公网服务端S. 如果A和B ...
- TCP打洞和UDP打洞的区别 (相互直接访问)
为什么网上讲到的P2P打洞基本上都是基于UDP协议的打洞?难道TCP不可能打洞?还是TCP打洞难于实现? 假设现在有内网客户端A和内网客户端B,有公网服务端S. 如果A和B想要进行UD ...
- 10 TCP限流技术
TCP限流是因为让接收方充分接受完消息,保证数据安全,不会丢失 一.窗口机制介绍 发送端和接收端都拥有一个窗口,当发送端发送数据时,落进窗口的数据被发送,当接受端接受数据时,落进接收端窗口的数据将会被 ...
- 用TCP穿透NAT(TCP打洞)的实现
目录 TCP穿透原理 程序思路 声明 上代码 运行示例 1. TCP穿透原理: 我们假设在两个不同的局域网后面分别有2台客户机A和 B,AB所在的局域网都分别通过一个路由器接入互联网.互联网上有一台服 ...
随机推荐
- oracle ash性能报告的使用方法
活动会话历史报告活动会话历史v$active_session_history视图提供了在实例级别抽取会话活动信息.活动会话每分钟会被抽样一次且被存储在sga中的循环缓冲区中.任何被连接到数据库且正等待 ...
- 杂项:Kafka
ylbtech-杂项:Kafka Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写.Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站 ...
- [.Net] C# Excel操作类 ExcelHelper
实现C#与Excel文件的交互操作,实现以下功能: 1.DataTable 导出到 Excel文件 2.Model数据实体导出到 Excel文件[List<Model>] 3.导出数据到模 ...
- Spark Scala语言学习系列之完成HelloWorld程序(三种方式)
三种方式完成HelloWorld程序 分别采用在REPL,命令行(scala脚本)和Eclipse下运行hello world. 一.Scala REPL. windows下安装好scala后,直接C ...
- TensorFlow-LSTM序列预测
问题情境:已知某一天内到目前为止股票各个时刻的价格,预测接下来短时间内的价格变化. import tushare as ts import time from collections import n ...
- DeepMind用ReinforcementLearning玩游戏
原文 : http://dataunion.org/?p=639 1.引言 说到机器学习最酷的分支,非Deep learning和Reinforcement learning莫属(以下分别简称DL和 ...
- 8 Python+Selenium操作测试对象
[环境信息] Python3.6+selenium3.0.2+Firefox50.0+win7 [操作方法] 1.清除输入框内容:clear() 2.单击一个按钮:click() 3.返回元素尺寸:s ...
- 关于maven-基本
笔记 Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具. Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object ...
- 开启RxSwift之旅——开篇
开启RxSwift之旅——开篇 RxSwift 是 ReactiveX 在 Swift 下的实现.ReactiveX 是一个通过使用可观察序列来组合异步和基于事件的程序的库. 很多地方通常把 Reac ...
- <td colspan="6"></td>代表这个td占6个td的位置
<td colspan="6"><span class="order-time">2017-10-11 14:55:23</spa ...