TCP实现P2P通信

Internet的迅速发展以及IPv4 地址数量的限制使得网络地址翻译(NAT,Network Address Trans2lation)设备得到广泛应用。NAT设备允许处于同一NAT后的多台主机共享一个公网(本文将处于同一NAT后的网络称为私网,处于NAT前的网络称为公网) IP 地址。一个私网IP 地址通过NAT设备与公网的其他主机通信。公网和私网IP地址域,如下图所示:

INTERNET

202.103.142.29

NAT-A

192.168.0.1

 

电脑A-01

192.168.0.2

电脑A-02

192.168.0.3

 

电脑A-01

192.168.1.12

 

电脑A-02

192.168.1.13

 

Server ( WebFTPPOP3SMTPIISDatabase … )

129.208.12.38

221.10.145.84

NAT-B

192.168.1.1

 

广域网与私网示意图

一般来说都是由私网内主机(例如上图中“电脑A-01”)主动发起连接,数据包经过NAT地址转换后送给公网上的服务器(例如上图中的“Server”),连接建立以后可双向传送数据,NAT设备允许私网内主机主动向公网内主机发送数据,但却禁止反方向的主动传递,但在一些特殊的场合需要不同私网内的主机进行互联(例如P2P软件、网络会议、视频传输等),TCP穿越NAT的问题必须解决。网上关于UDP穿越NAT的文章很多,而且还有配套源代码,但是我个人认为UDP数据虽然速度快,但是没有保障,而且NATUDP准备的临时端口号有生命周期的限制,使用起来不够方便,在需要保证传输质量的应用上TCP连接还是首选(例如:文件传输)。

网上也有不少关于TCP穿越NAT(即TCP打洞)的介绍文章,但不幸我还没找到相关的源代码可以参考,我利用空余时间写了一个可以实现TCP穿越NAT,让不同的私网内主机建立直接的TCP通信的源代码。

 

这里需要介绍一下NAT的类型:

NAT设备的类型对于TCP穿越NAT,有着十分重要的影响,根据端口映射方式,NAT可分为如下4,3NAT类型可统称为cone类型。

(1)全克隆( Full Cone) : NAT把所有来自相同内部IP地址和端口的请求映射到相同的外部IP地址和端口。任何一个外部主机均可通过该映射发送IP包到该内部主机。

(2)限制性克隆(Restricted Cone) : NAT把所有来自相同内部IP地址和端口的请求映射到相同的外部IP地址和端口。但是,只有当内部主机先给IP地址为X的外部主机发送IP,该外部主机才能向该内部主机发送IP包。

(3)端口限制性克隆( Port Restricted Cone) :端口限制性克隆与限制性克隆类似,只是多了端口号的限制,即只有内部主机先向IP地址为X,端口号为P的外部主机发送1IP,该外部主机才能够把源端口号为PIP包发送给该内部主机。

(4)对称式NAT ( Symmetric NAT) :这种类型的NAT与上述3种类型的不同,在于当同一内部主机使用相同的端口与不同地址的外部主机进行通信时, NAT对该内部主机的映射会有所不同。对称式NAT不保证所有会话中的私有地址和公开IP之间绑定的一致性。相反,它为每个新的会话分配一个新的端口号。

 

我们先假设一下:有一个服务器S在公网上有一个IP,两个私网分别由NAT-ANAT-B连接到公网,NAT-A后面有一台客户端ANAT-B后面有一台客户端B,现在,我们需要借助SAB建立直接的TCP连接,即由BA打一个洞,让A可以沿这个洞直接连接到B主机,就好像NAT-B不存在一样。

实现过程如下(请参照源代码):

1、  S启动两个网络侦听,一个叫【主连接】侦听,一个叫【协助打洞】的侦听。

2、  AB分别与S的【主连接】保持联系。

3、  A需要和B建立直接的TCP连接时,首先连接S的【协助打洞】端口,并发送协助连接申请。同时在该端口号上启动侦听。注意由于要在相同的网络终端上绑定到不同的套接字上,所以必须为这些套接字设置 SO_REUSEADDR 属性(即允许重用),否则侦听会失败。

4、  S的【协助打洞】连接收到A的申请后通过【主连接】通知B,并将A经过NAT-A转换后的公网IP地址和端口等信息告诉B

5、  B收到S的连接通知后首先与S的【协助打洞】端口连接,随便发送一些数据后立即断开,这样做的目的是让S能知道B经过NAT-B转换后的公网IP和端口号。

6、  B尝试与A的经过NAT-A转换后的公网IP地址和端口进行connect,根据不同的路由器会有不同的结果,有些路由器在这个操作就能建立连接(例如我用的TPLink R402),大多数路由器对于不请自到的SYN请求包直接丢弃而导致connect失败,但NAT-A会纪录此次连接的源地址和端口号,为接下来真正的连接做好了准备,这就是所谓的打洞,即BA打了一个洞,下次A就能直接连接到B刚才使用的端口号了。

7、  客户端B打洞的同时在相同的端口上启动侦听。B在一切准备就绪以后通过与S的【主连接】回复消息“我已经准备好”,S在收到以后将B经过NAT-B转换后的公网IP和端口号告诉给A

8、  A收到S回复的B的公网IP和端口号等信息以后,开始连接到B公网IP和端口号,由于在步骤6B曾经尝试连接过A的公网IP地址和端口,NAT-A纪录了此次连接的信息,所以当A主动连接B时,NAT-B会认为是合法的SYN数据,并允许通过,从而直接的TCP连接建立起来了。


  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用 TCP 连接实现 P2P (点对点) 通信,你可以使用 Node.js 内置的 `net` 模块。 以下是一些实现步骤: 1. 首先,你需要创建一个 TCP 服务器,让其他节点可以连接到它。你可以使用 `net.createServer()` 方法来创建一个服务器,该方法接受一个回调函数作为参数,该回调函数将在每次有新连接时被调用。 ``` const net = require('net'); const server = net.createServer((socket) => { console.log('New client connected'); }); ``` 2. 接下来,你需要让服务器开始监听传入的连接。你可以使用 `server.listen()` 方法来做到这一点,该方法接受一个端口号和一个可选的回调函数作为参数。 ``` server.listen(3000, () => { console.log('Server started on port 3000'); }); ``` 3. 现在,你需要创建一个 TCP 客户端,它将连接到服务器。你可以使用 `net.connect()` 方法来创建一个客户端连接,该方法接受一个端口号和一个 IP 地址作为参数。 ``` const client = net.connect({ port: 3000, host: 'localhost' }, () => { console.log('Connected to server'); }); ``` 4. 当客户端连接到服务器时,服务器的回调函数将被调用,并且你可以在其中执行任何你想要的操作,例如将客户端添加到一个数组中以便稍后向它发送消息。 ``` const clients = []; const server = net.createServer((socket) => { console.log('New client connected'); clients.push(socket); // Handle incoming data from the client socket.on('data', (data) => { console.log(`Received data from client: ${data}`); }); }); ``` 5. 最后,你可以使用 `socket.write()` 方法将消息发送到连接到服务器的某个客户端。该方法接受一个字符串或一个缓冲区作为参数。 ``` clients[0].write('Hello, client!'); ``` 以上是使用 TCP 连接实现 P2P 通信的基本步骤,你可以根据你的具体需求进行调整和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值