在制作一款私有云网络存储产品时,很多客户反应,他们希望能远程访问。远程访问内网服务器成本最低的方式应该就是P2P内网穿透了,这里介绍一下实现原理。
UDP穿透原理
打洞主要采用的是udp的无面向连接的特性来实现,同过user1连接server,打通user1的对外ip和端口,然后在一段时间内server都能通过这个端口和IP实现路由穿透,向局域网内的服务器发送消息。
此时user2连接server,打通uer2对外的ip和端口,然后user1和user2通过彼此已经打通的对外ip和端口实现通讯。
这就是P2P不同网络间通信协议的基础。
为了简单起见,这只仅演示从 user1 向 server 打洞穿透的过程。需要先准备一台服务器和一台本地电脑。
UDP打洞源代码
因为UDP是既可以侦听又可以发送数据的,所以node.js的代码实现非常简单,服务器和本地用一套代码即可。命名为 onceair.udp.server.js 此文件接收命令行参数,侦听本地 8090 端口:
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')
}
}
示例
将 udp.server 上传到服务器端,然后运行在后台运行,默认侦听 8090 端口
/root/node/bin/node onceair.udp.server.js
在客户端向服务器的 8090 发送消息,同时本地侦听 8090
$ node onceair.udp.server.js 8090 112.124.126.185
服务器端接收到响应,我们看到这里接收到的客户端公网IP是180.173.98.126,端口号是 43191,这是路由转出消息的随机端口,通过这个端口,就能连接到本地UDP侦听的8090端口,即是一个临时的session通道。
message
echo
{ address: '180.173.98.126',
family: 'IPv4',
port: 43191,
size: 4 }
Ctrl + C 中上 onceair.udp.server.js 进程,然后向这个IP和穿透的端口发送数据包,记住操作时间不要太长,否则打通的 session 可能会过期。
/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
{}
本地接收到的消息,代表通过已经打通
message
echo
{ address: '112.124.126.185',
family: 'IPv4',
port: 8090,
size: 4 }
TCP的内网穿透
实现基于TCP协议的p2p“打洞”过程中,最主要的问题不是来自于TCP协议,而是来自于来自于应用 程序的API接口。这是由于标准的伯克利(Berkeley)套接字的API是围绕着构建客户端/服务器程序 而设计的,API允许TCP流套接字通过调用connect()函数来建立向外的连接,或者通过listen()和 accept函数接受来自外部的连接,但是,API不提供类似UDP那样的,同一个端口既可以向外连接, 又能够接受来自外部的连接。而且更糟的是,TCP的套接字通常仅允许建立1对1的响应,即应用程 序在将一个套接字绑定到本地的一个端口以后,任何试图将第二个套接字绑定到该端口的操作都会 失败。
因此要实现基于TCP的内网穿透,在内网架设一台HTTP网站,要比UDP复杂得多。
加粗文本
组播可用内网穿透吗
node.js 视频教程 http://element-ui.cn/article/show-22239.shtml
node onceair.udp.server.js 8090 112.124.126.185 后面的ip是客户端ip吗?
This is Privoxy 3.0.26 on PC-20181104GAPK (127.0.0.1), port 25378 No server or forwarder data received Your request for http://78.141.203.6/ could not be fulfilled, because the connection to 78.141.203.6 (127.0.0.1) has been closed before Privoxy received any data for this request.
This is often a temporary failure, so you might just try again.
If you get this message very often, consider disabling connection-sharing (which should be off by default). If that doesn't help, you may have to additionally disable support for connection keep-alive by setting keep-alive-timeout to 0.
More Privoxy: Privoxy main page Shut down - Do not deploy this build in a production environment, this is a one click Denial Of Service attack!!! View the current configuration View the source code version numbers View or toggle the tags that can be set based on the clients address View the request headers Look up which actions apply to a URL and why View the proxy-auto-config(PAC) script. Documentation Support and Service: The Privoxy Team values your feedback.
Please have a look at the User Manual to learn how to get support or report problems.
If you want to support the Privoxy Team, you can participate or donate.
没有任何用