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)的更多相关文章

  1. TCP点对点穿透探索--失败

    TCP点对点穿透探索 点对点穿透是穿透什么 点对点穿透,需要实现的是对NAT的穿透.想实现NAT的穿透,当然要先了解NAT到底是什么,以及NAT是用来干什么的.NAT全称Network Address ...

  2. Nginx配置TCP请求转发

    Nginx配置TCP请求转发 1.TCP请求转发基于stream在1.9版本前,需要单独编译安装该组建: # 依赖服务 [root@baolin conf]#yum -y install pcre-d ...

  3. TCP端口转发(centos7)

    =============================================== 2019/2/14_第1次修改                       ccb_warlock == ...

  4. 使用 ssh -R 建立反向/远程TCP端口转发代理

    转自:https://yq.aliyun.com/articles/8469 ssh是一个非常棒的工具, 不但能建立动态转发, 例如chrome的Switchy插件用到的就是这个技术.http://b ...

  5. Haproxy TCP数据转发

    在实际项目中需要用到haproxy做TCP转发,下面主要针对haproxy的安装及TCP数据转发配置进行说明 一.安装Haproxy (1)编译安装Haproxy mkdir -p /data01/h ...

  6. windows操作系统自带的TCP端口转发

    假定需要通过192.168.1.8的14941端口连接192.168.1.118的1494端口,则需要在192.168.1.8主机的命令行输入如下语句netsh  interface ipv6 ins ...

  7. TCP/IP协议族——IP工作原理及实例具体解释(上)

     IP协议具体解释 本文主要介绍了IP服务特点,头部结构,IP分片知识,并用tcpdump抓取数据包.来观察IP数据报传送过程中IP的格式,以及分片的过程. IP头部信息:IP头部信息出如今每一个 ...

  8. tcp/iP协议族——IP工作原理及实例具体解释(下)

     IP协议具体解释 上一篇文章文章主要介绍了IP服务的特点,IPv4头部结构IP分片.并用tcpdump抓取数据包,来观察IP数据报传送过程中IP的格式,以及分片的过程.本文主要介绍IP路由,IP ...

  9. TCP面试题之滑动窗口原理

    TCP 滑动窗口 作用: 1. 提供TCP可靠性:对发送的数据进行确认 2. 流量控制:窗口大小随链路变化 一.TCP窗口机制 TCP中窗口大小是指tcp协议一次传输多少个数据.因为TCP是一个面向连 ...

随机推荐

  1. Asp.NetCore轻松学-使用Docker进行容器化托管

    前言 没有 docker 部署的程序是不完整的,在写了 IIS/Centos/Supervisor 3篇托管介绍文章后,终于来到了容器化部署,博客园里面有关于 docker 部署的文章比比皆是,作为硬 ...

  2. NET微信公众号开发环境搭建(一)-了解微信由来

    公众号的应用,开发及调试环境搭建 花生壳要注册 需要二十多块钱 ,还要实名认证,估计要一两天才能审核通过  主要就是在 windows搭建测试环境   1.微信的应用场景   360百科微信简介 ht ...

  3. 设计模式之Singleton模式和Strategy模式是什么

    Singleton模式 单例模式,也交单子模式,有时候系统只需要拥有一个全局对象. 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建.这个类提供了一种访问其唯一的对象的方 ...

  4. 2017-12-24 为新语言编写Visual Studio Code语法高亮插件

    本文源码库: program-in-chinese/quan4-highlighter 语法高亮是一个开发环境的基本功能. 此文尝试为之前的"圈4"语言(详见编程语言试验之Antl ...

  5. SQL 高效运行注意事项(一)

    设计SQL后,应使用explain命令检查SQL,看是否使用到索引,是否存在filesort,重点检查检索的行数(rows)是否太大. 一般来说. 1.rows<1000,是在可接受的范围内的. ...

  6. Visual Studio Code-批量添加或删除注释行

    小技巧一例,批量删除Visual Studio code或notepad++注解信息,便于读取有效代码或文本信息,具体操作如下: Visual Studio Code批量删除注解行信息: 在VS Co ...

  7. Luogu P5284 [十二省联考2019]字符串问题

    好难写的字符串+数据结构问题,写+调了一下午的说 首先理解题意后我们对问题进行转化,对于每个字符串我们用一个点来代表它们,其中\(A\)类串的点权为它们的长度,\(B\)类串的权值为\(0\) 这样我 ...

  8. VS2017搭建驱动开发环境WDK

    先安装VS2017,然后在安装WDK,WDK会自动关联到VS2017中,不用你任何操作,自动在新建项目中可以找到驱动开发. 如果以上安装完成后,在VS2017中新建项目中没有发现WDK,那么需要进行修 ...

  9. T5大牛带你解析:如何实现分布式技术

    1.分布式事务 2. 分布式锁 Java 原生 API 虽然有并发锁,但并没有提供分布式锁的能力,所以针对分布式场景中的锁需要解决的方案. 分布式锁的解决方案大致有以下几种: 基于数据库实现 基于缓存 ...

  10. Python 爬虫——抖音App视频抓包

    APP抓包 前面我们了解了一些关于 Python 爬虫的知识,不过都是基于 PC 端浏览器网页中的内容进行爬取.现在手机 App 用的越来越多,而且很多也没有网页端,比如抖音就没有网页版,那么上面的视 ...