Node.JS中UDP打洞穿透内网路由,架设内网服务器技术详解及源码
CliParamor
7年前
<p>在制作一款私有云网络存储产品时,很多客户反应,他们希望能远程访问。远程访问内网服务器成本最低的方式应该就是P2P内网穿透了,这里介绍一下实现原理。</p> <h3>UDP穿透原理</h3> <p>打洞主要采用的是udp的无面向连接的特性来实现,同过user1连接server,打通user1的对外ip和端口,然后在一段时间内server都能通过这个端口和IP实现路由穿透,向局域网内的服务器发送消息。</p> <p>此时user2连接server,打通uer2对外的ip和端口,然后user1和user2通过彼此已经打通的对外ip和端口实现通讯。</p> <p>这就是P2P不同网络间通信协议的基础。</p> <p>为了简单起见,这只仅演示从 user1 向 server 打洞穿透的过程。需要先准备一台服务器和一台本地电脑。</p> <h3>UDP打洞源代码</h3> <p>因为UDP是既可以侦听又可以发送数据的,所以node.js的代码实现非常简单,服务器和本地用一套代码即可。命名为 onceair.udp.server.js 此文件接收命令行参数,侦听本地 8090 端口:</p> <pre> var os = require('os') var fs = require('fs') var net = require('net') var dgram = require('dgram') var cp = require('child_process') var path = require('path') var udp = dgram.createSocket('udp4') udp.on('message', function(data, ipdr) { console.log('message') console.log(data.toString()) console.log(ipdr) }) udp.on('error', function(err) { console.log('error') console.log(arguments) }) udp.on('listening', function(err) { console.log('listening') console.log(arguments) }) udp.bind(8090) console.log('udp listening on port 8090') var send = function(message, port, host) { console.log('send') console.log(arguments) udp.send(Buffer.from(message), port || 8090, host || 'anynb.com') } //called directly in command line if (require.main === module) { var port = parseInt(process.argv[2]) var host = process.argv[3] if (port) { send('echo', port, host) } else { send('echo') } }</pre> <h3>示例</h3> <p>将 udp.server 上传到服务器端,然后运行在后台运行,默认侦听 8090 端口</p> <pre> /root/node/bin/node onceair.udp.server.js</pre> <p>在客户端向服务器的 8090 发送消息,同时本地侦听 8090</p> <pre> $ node onceair.udp.server.js 8090 112.124.126.185</pre> <p>服务器端接收到响应,我们看到这里接收到的客户端公网IP是180.173.98.126,端口号是 43191,这是路由转出消息的随机端口,通过这个端口,就能连接到本地UDP侦听的8090端口,即是一个临时的session通道。</p> <pre> message echo { address: '180.173.98.126', family: 'IPv4', port: 43191, size: 4 }</pre> <p>Ctrl + C 中上 onceair.udp.server.js 进程,然后向这个IP和穿透的端口发送数据包,记住操作时间不要太长,否则打通的 session 可能会过期。</p> <pre> /root/node/bin/node onceair.udp.server.js 43191 180.173.98.126 udp listening on port 8090 send { '0': 'echo', '1': 43191, '2': '180.173.98.126' } listening {}</pre> <p>本地接收到的消息,代表通过已经打通</p> <pre> message echo { address: '112.124.126.185', family: 'IPv4', port: 8090, size: 4 }</pre> <h3>TCP的内网穿透</h3> <p>实现基于TCP协议的p2p“打洞”过程中,最主要的问题不是来自于TCP协议,而是来自于来自于应用 程序的API接口。这是由于标准的伯克利(Berkeley)套接字的API是围绕着构建客户端/服务器程序 而设计的,API允许TCP流套接字通过调用connect()函数来建立向外的连接,或者通过listen()和 accept函数接受来自外部的连接,但是,API不提供类似UDP那样的,同一个端口既可以向外连接, 又能够接受来自外部的连接。而且更糟的是,TCP的套接字通常仅允许建立1对1的响应,即应用程 序在将一个套接字绑定到本地的一个端口以后,任何试图将第二个套接字绑定到该端口的操作都会 失败。</p> <p>因此要实现基于TCP的内网穿透,在内网架设一台HTTP网站,要比UDP复杂得多。</p> <p> </p> <p>来自:http://ourjs.com/detail/5a4c46fe3506837194998bda</p> <p> </p>