TCP点对点转发的实现与原理(nodejs)
2019-02-25 16:10:31来源:博客园 阅读 ()
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)
原文链接:https://www.cnblogs.com/fyter/p/nat-agent-nagent-frp.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- js防止表单重复提交实现代码 2020-03-29
- 基于JQuery的多标签实现代码 2020-03-29
- js实现翻页后保持checkbox选中状态的实现方法 2020-03-25
- NiftyCube实现圆角边框的方法 2020-03-20
- JS实现标签页切换效果 2020-03-12
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash