TCP点对点转发的实现与原理(nodejs)
Nagent
Nagent是TCP点对点转发实现,名称来源于Nat与Agent的组合。类似frp项目,可以在局域网与互联网提供桥梁。
前提是你要有一台流量服务器并且有一个公网IP。如果没有,也可以找服务商。
暂不能向frp那样为HTTP服务,但可以实现简单的分发————你只需要在两台内网HTTP服务器上运行Nagent客户端即可。
项目位置:https://github.com/FettLuo/nagent
进度
可以使用
未向特殊协议优化,例如http/s的转发
虽然协议有涉及账号名与密码,但未实现
未来希望你或我,向项目添加账号管理支持,以webservice的形式支持
名词解释
客户端:运行在内网的Nagent客户端。
服务端:运行在公网服务器上的Nagent服务端。
用户:互联网上的实际用户。
过程
服务器监听在5670端口(默认)。
客户端配置好自己的服务端口,也可以指定内网其他计算机。假设本机80端口。
客户端登录到服务器,通知服务器我需要监听的外网端口,比如90。
一切正常的话(防火墙没问题,端口没被占用等),服务器上90端口的连接即会被导向到内网的80端口服务上。
原理
客户端与服务器保持着一定数量的连接,每个连接都需要登录成功。
用户连接公网服务器的端口后会从客户端的列表中弹出一个用于数据转发。
当客户端第一次收到数据时,建立与本地服务的连接,并发送/转发数据。
部署
需要NodeJS
运行为服务端
windows/linux:
node nagent.js -s
linux:
./nagent.js -s
运行为客户端
windows/linux:
node nagent.js -p 90 -P 80
linux:
./nagent.js -p 90 -P 80
配置
保存下面内容到nagent.js所在的目录,文件名为nagent.config,方括号内替换为你的参数。
local_port=[你的本地服务端口]
server_port=5670// 服务端端口号
server_host='[服务端的主机地址,IP或域名均可]'
remote_port=[你需要服务端为你开放的公网端口]
keep_conn_count=10// 同时保持的最大连接数量
客户端代码
// handling login
var handling_login=(sock,data)=>{
if(data.toString()=="ok\r"){
log("login is done.", sock.remoteAddress+":"+sock.remotePort)
sock.on("data", d=>{handling_data(sock,d)})
}else{
log("login is failed.", sock.remoteAddress+":"+sock.remotePort, data.toString())
sock.end()
}
} // handling data
var handling_data=(sock,data)=>{
if(!sock.partner){
if(!sock.buffer)// save data for connect done
sock.buffer=data
else{
sock.buffer=Buffer.concat([sock.buffer,data])
return
}
partner = net.connect(local_port, local_host)
conn_count--
partner.on("connect", e=>{
log("partner connect is done. port is", local_port)
debug("s>>", show(sock.buffer))
partner.write(sock.buffer)
sock.partner=partner
})
partner.on("data", d=>{debug("s<<",show(d));sock.write(d)})
partner.on("error", e=>{open_conn()})
partner.on("end",e=>{log("partner is closed", local_port);sock.end()})
}else{
debug("s>>", show(data))
sock.partner.write(data)
}
} // open service
var open_conn=port=>{
if(conn_count>=keep_conn_count)return
var temp=net.connect(server_port, server_host)
conn_count+=1
temp.on("connect", e=>{
log(temp.remoteAddress, temp.localPort, "connected! current connection count is", conn_count)
temp.write("NAGENT1.0 guest nopwd "+remote_port+"\r")// protocal,username,password(can't include space char),open port
temp.once("data", d=>{handling_login(temp,d)})
if(conn_count<keep_conn_count)open_conn()
})
temp.on("error", e=>{conn_count--;log(e.errno);setTimeout(open_conn, 1000)})
temp.on("end", e=>{
conn_count--
log("connection is closed", temp.remotePort, "connnection count:", conn_count)
if(temp.partner)temp.partner.end()
setTimeout(open_conn, 1000)
})
} open_conn()
服务端代码
var ports={}// port=>server(clients)
var debug=e=>{}//console.warn // connection was closed
var disconnect=(server,s)=>{
if(s.client){
server.conns.delete(s.client)
s.client.destroy()
}else if(s.partner){
s.partner.destroy()
} if(server.conns.size+server.clients.length==0){
log("server was stop port is ", server.localPort)
server.close()
ports[server.localPort]=undefined
}
log(s.remotePort+" was closed")
} // open port
var open_port=(port,c)=>{
c.on("end", e=>{disconnect(server,c)})
c.on("error", e=>{log(e.errno, c.remotePort)})
if(ports[port]){
s=ports[port]
s.clients.push(c)
if(s.done)c.write("ok\r")
return
}
log("open port...",port)
var server=net.createServer()
ports[port]=server
server.done=false
server.clients=[c]
server.conns=new Set()// store used connection
server.listen(port)
server.on("connection", s=>{
console.log("new connection at", s.remoteAddress+":"+s.remotePort+">>:"+s.localPort)
if(server.clients.length==0){
log(port+"'s client is null")
s.end()
return
}
s.client = server.clients.pop()
s.client.partner = s
server.conns.add(s.client)
log("alloc",s.client.remoteAddress+":"+s.client.remotePort)
s.client.on("data", d=>{
debug(">>u",d.toString("hex"))
try{s.write(d)}catch(e){}
})
s.on("data", d=>{debug(">>c",d.toString("hex"));s.client.write(d)})
s.on("end", e=>{disconnect(server,s)})
s.on("error", e=>{disconnect(server,s)})
})
server.on("error", e=>{
console.log(e.errno)
for(var cli of server.clients){
cli.write("failed "+e.errno+"\r")
cli.end()
}
ports[port]=undefined
})
server.on("listening", e=>{
console.log("listen successful.",port)
server.done=true
for(var cli of server.clients){
log("notify ok!", cli.remoteAddress, cli.remotePort)
cli.write("ok\r")
}
})
} var client_connect=c=>{
c.on("data", d=>{
try{
s=d.toString()
segs=s.slice(0,-1).split(" ")
if(segs.length!=4 || segs[0]!="NAGENT1.0"){
log("login failed",s)
c.write("failed\r")
c.end()
return
}
}catch(e){
log("login exception:",e)
c.write("except\r")
c.end()
return
}
log("login successful!",segs[1])
c.removeAllListeners()
open_port(parseInt(segs[3]), c)
})
log("new client at", c.remoteAddress, c.remotePort)
} var server=net.createServer(client_connect)
server.listen(server_port)
log("service listen at", server_port)
TCP点对点转发的实现与原理(nodejs)的更多相关文章
- TCP点对点穿透探索--失败
TCP点对点穿透探索 点对点穿透是穿透什么 点对点穿透,需要实现的是对NAT的穿透.想实现NAT的穿透,当然要先了解NAT到底是什么,以及NAT是用来干什么的.NAT全称Network Address ...
- Nginx配置TCP请求转发
Nginx配置TCP请求转发 1.TCP请求转发基于stream在1.9版本前,需要单独编译安装该组建: # 依赖服务 [root@baolin conf]#yum -y install pcre-d ...
- TCP端口转发(centos7)
=============================================== 2019/2/14_第1次修改 ccb_warlock == ...
- 使用 ssh -R 建立反向/远程TCP端口转发代理
转自:https://yq.aliyun.com/articles/8469 ssh是一个非常棒的工具, 不但能建立动态转发, 例如chrome的Switchy插件用到的就是这个技术.http://b ...
- Haproxy TCP数据转发
在实际项目中需要用到haproxy做TCP转发,下面主要针对haproxy的安装及TCP数据转发配置进行说明 一.安装Haproxy (1)编译安装Haproxy mkdir -p /data01/h ...
- windows操作系统自带的TCP端口转发
假定需要通过192.168.1.8的14941端口连接192.168.1.118的1494端口,则需要在192.168.1.8主机的命令行输入如下语句netsh interface ipv6 ins ...
- TCP/IP协议族——IP工作原理及实例具体解释(上)
IP协议具体解释 本文主要介绍了IP服务特点,头部结构,IP分片知识,并用tcpdump抓取数据包.来观察IP数据报传送过程中IP的格式,以及分片的过程. IP头部信息:IP头部信息出如今每一个 ...
- tcp/iP协议族——IP工作原理及实例具体解释(下)
IP协议具体解释 上一篇文章文章主要介绍了IP服务的特点,IPv4头部结构IP分片.并用tcpdump抓取数据包,来观察IP数据报传送过程中IP的格式,以及分片的过程.本文主要介绍IP路由,IP ...
- TCP面试题之滑动窗口原理
TCP 滑动窗口 作用: 1. 提供TCP可靠性:对发送的数据进行确认 2. 流量控制:窗口大小随链路变化 一.TCP窗口机制 TCP中窗口大小是指tcp协议一次传输多少个数据.因为TCP是一个面向连 ...
随机推荐
- Android Gradle defaultConfig详解及实用技巧
实际项目中,都会应用Android Gradle Plugin,根据实际中的项目模块的职责,可以具体应用如下四种插件类型. 1,apply plugin: 'com.android.applicati ...
- Java基础练习1(数据类型转换)
1.下列代码的输出结果是:()(单选) public static void main(String[] args){ double money = 3.0; money -= 2.9; System ...
- xmlString和map互转Util
目录 XmlAndMapUtil类 XmlAndMapUtil类 import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org ...
- 软件工程通用软件体系结构主机终端模式、B/S 、C/S 结构和多层分布式结构
软件系统的体系结构经历了主机终端模式.客户机/服务器(C/S)模式.浏览器/服务器(B/S)和多层分布式结构. 主机/终端结构: 早期计算机系统多是单机系统,多个用户是通过联网终端来访问的,没有网络的 ...
- Api管家系列(二):编辑和继承Class
上篇写了个大概,今天我详细说一下参数的编辑,废话不多说 先打开一个项目,我要特别说一下设置里的“默认参数设置” 打开默认参数设置,这里我用红色圈出的tab可以设置请求头,返回头和返回状态,这些设置会在 ...
- cmd命令重定向到剪切板
Windows下 使用系统自带的 clip 命令. # 位于 C:\Windows\system32\clip.exe. 示例: # 将字符串 Hello 放入 Windows 剪贴板 echo He ...
- NLP第八条
今日!我虽啥也没干,但我还是有着学习的心的~ 也许是“遵义会议”呢 那也说不定 留下这句话再说 哈哈哈哈哈哈 只能抄抄NLP第八条了 谁叫我选了个灰常有意义的通识课咧 八,每一 ...
- nginx 安装、启动、重启、关闭 (linux系统命令行)
前言: 最近在部署我的hexo静态博客到腾讯云服务器上,用到了很多nginx的知识,在此做下总结: 刚接触的linux服务器上,nginx配置乱的有点令人发指,就把老的卸载了重新装一下. 1.卸载 y ...
- 华为云.NET Core支持情况调查
各大公有云都提供了开发者开发的SDK,今天我们来看看华为云对.NET Core的支持情况怎么样? .NET SDK地址 https://developer.huaweicloud.com/sdk#.N ...
- Linux 桌面玩家指南:01. 玩转 Linux 系统的方法论
特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...