你的位置:首页 > 互联网IT
Nodejs实现简单的TCP内网穿透,Nodejs内网穿透,Node内网穿透,NodejsTCP内网穿透,NodeTCP内网穿透,内网穿透
支持系统:Windows 64/32位系统/Linux系统
功能:NodejsTCP内网穿透
软件:node
性能:一般
Windows/linux下载地址:NodejsTCP内网穿透
windows node 下载地址:node-v12.14.0-x86.rar
项目地址:
https://gitee.com/baojuhua/node_simples/tree/master/node_simple_tcp_penetration
另一个内网穿透:NodejsTCP内网穿透
https://www.zhuguodong.com/?id=601
NodejsTCP内网穿透
用法:
服务器端
修改server.js:
const CONFIG = {
server_addr: "0.0.0.0",//服务端地址 一般是0.0.0.0或127.0.0.1
server_port: 10001,//服务端端口
token: "123456",//连接令牌
};
客户端
修改client.js:
const CONFIG = {
server_addr: "www.baidu.com",//服务端地址
server_port: 10001,//服务端端口
token: "123456",//连接令牌
timeout: 3000,//请求超时时间
interval: 5000,//异常重试,建议设置1000
binds: {//绑定内网应用
ssh: { //名称,多条名称设置不一样
local_ip: "www.baidu.com",//当前内网被转发的ip
local_port: 808,//当前内网被转发的端口
remote_port: 808,//服务端映射的端口
},
aria2: {
local_ip: "www.qq.com",//当前内网被转发的ip
local_port: 8080,//当前内网被转发的端口
remote_port: 8080,//服务端映射的端口
},
aria2Ng: {
local_ip: "8.8.8.8",//当前内网被转发的ip
local_port: 999,//当前内网被转发的端口
remote_port: 999,//服务端映射的端口
}
}
};
Windows/linux启动:
服务端
node server.js
客户端
node client.js
Windows系统:
安装:node-v12.14.0-x86.msi
查看:node -v
返回版本信息,比喻:v0.10.48
同目录下运行cmd:
服务端
node server.js或者启动server.bat
客户端
node client.js或者启动client.bat
后台隐藏运行:https://www.zhuguodong.com/?id=520
Linux系统:
第一种:nodejs安装
安装:yum -y install nodejs
查看:node -v
返回版本信息,比喻:v0.10.48
同目录下运行linux命令:
服务端
node server.js
客户端
node client.js
后台隐藏运行:https://www.zhuguodong.com/?id=498
1.升级node,下载 n管理包
npm install -g n
2.执行n 命令升级到v14.16.0
v14.16.0版本2021年2月23日
n 14.16.0 stable
v12.21.0版本2021年2月23日
n 12.21.0 stable
v10.23.3版本2021年2月9日
n 10.23.3 stable
3.重启centos7(必须).查看node版本,更新到14.16.0
reboot
第二种:nodejs安装
安装宝塔网站面板
软件商店:搜索node,安装
查看:node -v
返回版本信息,比喻:v0.10.48
Linux系统node安装:
参考文章:https://www.zhuguodong.com/?id=529
参考文章:
https://gitee.com/baojuhua/node_simples/tree/master/node_simple_tcp_penetration
https://www.jianshu.com/p/86dd17dea84d
Nodejs实现简单的TCP内网穿透
简书:https://www.jianshu.com/p/86dd17dea84d
网上关于TCP内网穿透的软件或是文章挺多的,其中通过TCP端口代理转发可以说是实现穿透最简单的方式了。
注意:文章代码只是原理示例,并没有考虑性能、安全性以及一些异常的处理,请切勿在生产环境中使用。
1.原理解析
首先了解一下,普通端口代理转发,与内网穿透的端口代理转发的区别
为了方便大家理解,我将IP换成应用,效果如下

整体的效果大概是这个样子的
1.1.具体步骤
首先穿透客户端连接上穿透服务端
TCP连接上后提交校验数据,与一些配置信息
服务端校验成功后,服务端根据配置信息开放对应的端口监听服务
注意:接下来这一步有两种做法
假设服务端有内网客户端访问到某个端口了
我一开始是直接将请求数据通过已经连接好的Socket连接发送给穿透客户端
然后,穿透客户端动态创建访问客户端去连接内网的服务或App
结果我发现,上面这种做法在Socket标识上需要做好多处理,而且可能涉及到修改数据包的操作
于是我改用下面的这种做法
穿透客户端只负责提交配置数据,与接收服务端发送的对应的端口访问请求
穿透客户端接收到请求后,开放两个新的客户端用于双向的数据传输
这样服务端与客户端的数据基本就打通了
2.编写穿透客户连接端程序与穿透服务端监听程序
根据原理编写起来其实就很简单了
注意:我这边为了方便理解,在示例代码中使用了json字符串来传输数据,实际情况一般都是使用字节
2.1.穿透客户端
通过net.createConnection
创建Socket连接,在连接成功后发送相关数据到服务端
代码:
const net = require('net');// 配置const CONFIG = { //一些配置....};// 用于校验请求的codelet CODE;// 创建用于连接校验服务端的 客户端连接let linkClient = net.createConnection({ port: CONFIG.server_port, host: CONFIG.server_addr }, () => { // 创建用于校验请求的code CODE = (+new Date()); // 发送数据校验 linkClient.write(JSON.stringify({ token: CONFIG.token, code: CODE, type: 'register', binds: CONFIG.binds })); console.log(`[${(new Date()).toLocaleString()}] 正在尝试连接...`);});linkClient.setTimeout(CONFIG.timeout);linkClient.on('data', (data) => { try { data = JSON.parse(data); // 校验请求 if (data.code == CODE) { if (data.type == 'register') { console.log(`[${(new Date()).toLocaleString()}] 已连接到服务器 ${CONFIG.server_addr}:${CONFIG.server_port}`); } else { // .... } return; } } catch (error) { // 异常 } return linkClient.end();});linkClient.on('error', (err) => { });linkClient.on('end', () => { });
2.2.穿透服务端
通过net.createServer
创建端口监听服务
在接收到数据对数据进行校验与绑定处理
const net = require('net');// 配置const CONFIG = { //...};// 客户端链接集let linkClients = {};// 客户端绑定的应用let clientBings = {};// 连接记录let socketRecords = {}; // 远程监听访问 用于穿透const listenServer = net.createServer((socket) => { let id = [socket.remoteAddress, socket.remoteFamily, socket.remotePort].join("_"); console.log(`[${(new Date()).toLocaleString()}] ${socket.remoteAddress}:${socket.remotePort} 上线...`); if (!linkClients[id]) { // 保存连接 linkClients[id] = socket; socket.on('data', (data) => { try { data = JSON.parse(data); let [token, type, code, binds] = [data.token, data.type, data.code, data.binds]; if (token != token) return socket.end(); if (type == 'register') {// 客户端注册 for (let k in binds) { let d = binds[k]; if (!clientBings[k]) { [d.code, d.name, d.linkSocket, d.linkId] = [data.code, k, socket, id]; //d.forwardServer = createForwardServer(d);//绑定客户端App对应服务端开放的端口服务 clientBings[k] = d; } } return socket.write(JSON.stringify({ code: code, type: type, id: id })); } } catch (error) { // removeInfo(id); } socket.end(); }); socket.on('error', (err) => { }) socket.on('end', (data) => { }); }});// 启动服务端监听listenServer.listen(CONFIG.server_port, CONFIG.server_addr);
3.完善功能
3.1.给穿透客户端添加动态创建Socket连接与异常重试功能
动态创建Socket很简单,就是根据服务端数据new Socket
两个,一个连接服务端,一个用来连接局域网的应用。
而异常重试功能只不过是加了个定时器进行定时状态检查罢了
代码:
const net = require('net');// 配置const CONFIG = { server_addr: "xxx.xxx.xxx.xxx",//服务端地址 server_port: 10001,//服务端端口 token: "hello server",//连接令牌 timeout: 3000,//请求超时时间 interval: 5000,//异常重试 binds: {//绑定内网应用 ssh: { local_ip: "192.168.199.193",//当前内网被转发的ip local_port: 22,//当前内网被转发的端口 remote_port: 10002,//服务端映射的端口 }, aria2: { local_ip: "192.168.199.193",//当前内网被转发的ip local_port: 6800,//当前内网被转发的端口 remote_port: 10003,//服务端映射的端口 }, aria2Ng: { local_ip: "127.0.0.1",//当前内网被转发的ip local_port: 80,//当前内网被转发的端口 remote_port: 10004,//服务端映射的端口 } }};// 用于校验请求的codelet CODE;let linkClient;function listenClient() { // 创建用于连接校验服务端的 客户端连接 linkClient = net.createConnection({ port: CONFIG.server_port, host: CONFIG.server_addr }, () => { // 创建用于校验请求的code CODE = (+new Date()); // 发送数据校验 linkClient.write(JSON.stringify({ token: CONFIG.token, code: CODE, type: 'register', binds: CONFIG.binds })); console.log(`[${(new Date()).toLocaleString()}] 正在尝试连接...`); }); linkClient.setTimeout(CONFIG.timeout); linkClient.on('data', (data) => { try { data = JSON.parse(data); // 校验请求 if (data.code == CODE) { if (data.type == 'register') { console.log(`[${(new Date()).toLocaleString()}] 已连接到服务器 ${CONFIG.server_addr}:${CONFIG.server_port}`); } else { // 请求标识 let key = data.key; // 应用名称 let name = data.name; // 本地的应用 let localApp = CONFIG.binds[name]; if (!localApp) linkClient.end(); // 创建服务端用的Socket let serverClient = new net.Socket(); serverClient.setTimeout(CONFIG.timeout); // 创建局域网内的Socket let localClient = new net.Socket(); localClient.setTimeout(CONFIG.timeout); // 连接服务端 serverClient.connect(CONFIG.server_port, CONFIG.server_addr, function () { serverClient.write(JSON.stringify({ type: 'connect', key: key })); // 连接本地服务器 localClient.connect(localApp.local_port, localApp.local_ip, function () { console.log(`[${(new Date()).toLocaleString()}] [${name}] ${localApp.local_port}<===>${localApp.remote_port}`); }); // 本地数据转发服务端 localClient.pipe(serverClient); localClient.on('end', function (data) { serverClient.end(); }); }) serverClient.on('error', function (err) { console.error(`[${(new Date()).toLocaleString()}] 访问服务器异常,${err.message}`); localClient.end(); }) localClient.on('error', function (err) { console.error(`[${(new Date()).toLocaleString()}] 局域网访问异常,${err.message}`); serverClient.end(); }); // 服务端数据转发本地 serverClient.pipe(localClient); serverClient.on('end', function (data) { localClient.end(); }); } return; } } catch (error) { // 异常 } return linkClient.end(); }); linkClient.on('error', (err) => { console.error(`[${(new Date()).toLocaleString()}] 异常:` + err.message); }); linkClient.on('end', () => { console.log(`[${(new Date()).toLocaleString()}] 已从服务器 ${CONFIG.server_port}:${CONFIG.server_port} 断开`); });}listenClient();//异常重试setInterval(() => { if (linkClient.readyState == "closed") { linkClient.end(); console.log(`[${(new Date()).toLocaleString()}] 正在重新连接服务器...`); listenClient(); }}, CONFIG.interval);
3.2.给穿透服务端添加绑定端口转发服务
服务端在Socket管理上,已经数据转发的过程上理解起来比客户端稍微复杂
代码:
const net = require('net');// 配置const CONFIG = { server_addr: "0.0.0.0",//服务端地址 一般是0.0.0.0或127.0.0.1 server_port: 10001,//服务端端口 token: "hello server",//连接令牌 };// 客户端链接集let linkClients = {};// 客户端绑定的应用let clientBings = {};// 连接记录let socketRecords = {};// 删除相关信息const removeInfo = function (id) { if (linkClients[id]) { linkClients[id] = null; delete linkClients[id]; } for (let k in clientBings) { let d = clientBings[k]; if (id == d.linkId) { d.forwardServer.close(); clientBings[k] = null; delete clientBings[k]; } }};// 端口转发服务 用于开放访问function createForwardServer(conf) { let forwardServer = net.createServer((socket) => { // 判断客户是否在线 if (!linkClients[conf.linkId]) return socket.end(); let id = [socket.remoteAddress, socket.remoteFamily, socket.remotePort].join("_"); if (!socketRecords[id]) socketRecords[id] = { bind: socket }; // 发送需要数据 告诉客户端 开启新的连接用于访问 conf.linkSocket.write(JSON.stringify({ key: id, name: conf.name, code: conf.code })); socket.on('error', () => { socket.end(); }); socket.on('end', () => { if (socketRecords[id]) { if (socketRecords[id].bind) socketRecords[id].client.end(); if (socketRecords[id].client) socketRecords[id].bind.end(); delete socketRecords[id]; } }) }); forwardServer.listen(conf.remote_port, () => { console.log(`[${(new Date()).toLocaleString()}] [${conf.name}] 转发服务开启 ${conf.local_port}<===>${conf.remote_port}`); }); return forwardServer;}// 远程监听访问 用于穿透const listenServer = net.createServer((socket) => { let id = [socket.remoteAddress, socket.remoteFamily, socket.remotePort].join("_"); console.log(`[${(new Date()).toLocaleString()}] ${socket.remoteAddress}:${socket.remotePort} 上线...`); if (!linkClients[id]) { // 保存连接 linkClients[id] = socket; socket.on('data', (data) => { try { data = JSON.parse(data); let [token, type, code, binds] = [data.token, data.type, data.code, data.binds]; if (token != token) return socket.end(); if (type == 'register') {// 客户端注册 for (let k in binds) { let d = binds[k]; if (!clientBings[k]) { [d.code, d.name, d.linkSocket, d.linkId] = [data.code, k, socket, id]; d.forwardServer = createForwardServer(d);//绑定客户端App对应服务端开放的端口服务 clientBings[k] = d; } } return socket.write(JSON.stringify({ code: code, type: type, id: id })); } else if (type == 'connect') {// 客户端连接 let id = data.key; let socketRecord = socketRecords[id]; if (socketRecord && socketRecord.bind && !socketRecord.client) { socket.firstConnection = false; socketRecord.client = socket;//设置 客户端连接soket // 数据转发 socket.pipe(socketRecord.bind); socketRecord.bind.pipe(socket); socketRecord.bind.on('end', () => { socket.end(); }); socketRecord.bind.on('error', (err) => { socket.end(); }); return; } } } catch (error) { // removeInfo(id); } socket.end(); }); socket.on('error', (err) => { // removeInfo(id); }) socket.on('end', (data) => { removeInfo(id); }); }});// 启动服务端监听listenServer.listen(CONFIG.server_port, CONFIG.server_addr);
发表评论: