Node.JS中UDP打洞穿透内网路由,架设内网服务器技术详解及源码

CliParamor 6年前
   <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>