当前位置: 首页 > news >正文

几行代码使用Go语言对接第三方工具之集成gin、kite框架、通过原生不借助其他软件实现内网穿透工具

几行代码使用Go语言对接第三方工具之集成gin、kite框架、通过原生不借助其他软件实现内网穿透工具。

在这里插入图片描述

内网穿透工具
多协程与通道配合达到快速响应
3秒发送一次心跳包维护连接
client断开自动重连

说明
server端: 具有公网地址的服务器
client端: 需要内网穿透的主机
使用
server端, 默认本地为5200端口

./server -l 5200 -r 3333

client端

./client -l 8080 -r 3333 -h 公网IP地址

用户访问 公网IP地址:5200 即可访问到 内网中的 8080端口程序


几行代码使用Go语言对接第三方工具之集成gin框架。

package mainimport ("fmt""github.com/gin-contrib/pprof""github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding""gopkg.in/go-playground/validator.v8""log""net/http""reflect"
)func main() {r := gin.Default()// 设置gin modegin.SetMode(gin.DebugMode)// 修改默认路由打印格式gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {log.Printf("【%v】====> %v\n", httpMethod, absolutePath)}// 全局中间件,阻止panicr.Use(gin.Recovery())// 为某个group下的路由注册中间件r.Group("/admin").Use(func(context *gin.Context) {if id := context.GetHeader("adminUserId"); id == "" {context.JSON(http.StatusForbidden, "请登录管理员")}})// 开启pprofpprof.Register(r)// 自定义校验// 将我们自定义的校验方法注册到 validator中if v, ok := binding.Validator.Engine().(*validator.Validate); ok {if err := v.RegisterValidation("NotNullAndAdmin", nameNotNullAndAdmin); err != nil {fmt.Println("RegisterValidation NotNullAndAdmin", err.Error())}}if err := r.Run(":8080"); err != nil {panic(err)}
}func nameNotNullAndAdmin(v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {if value, ok := field.Interface().(string); ok {// 字段不能为空,并且不等于  adminreturn value != "" && "admin" != value}return true
}type Req struct {Name string `validate:"NotNullAndAdmin"`
}

几行代码使用Go语言对接第三方工具之集成kite框架。

package mainimport "github.com/koding/kite"func main() {// Create a kitek := kite.New("math", "1.0.0")// Add our handler method with the name "square"k.HandleFunc("square", func(r *kite.Request) (interface{}, error) {a := r.Args.One().MustFloat64()result := a * a    // calculate the squarereturn result, nil // send back the result}).DisableAuthentication()// Attach to a server with port 3636 and run itk.Config.Port = 3636k.Run()
}

通过原生不借助其他软件实现内网穿透工具:

client/client.go

package mainimport ("context""flag""fmt""io""net""strings""time"
)var (host       stringlocalPort  intremotePort int
)func init() {flag.StringVar(&host, "h", "127.0.0.1", "remote server ip")flag.IntVar(&localPort, "l", 8080, "the local port")flag.IntVar(&remotePort, "r", 3333, "remote server port")
}type server struct {conn net.Conn// 数据传输通道read  chan []bytewrite chan []byte// 异常退出通道exit chan error// 重连通道reConn chan bool
}// 从Server端读取数据
func (s *server) Read(ctx context.Context) {// 如果10秒钟内没有消息传输,则Read函数会返回一个timeout的错误_ = s.conn.SetReadDeadline(time.Now().Add(time.Second * 10))for {select {case <-ctx.Done():returndefault:data := make([]byte, 10240)n, err := s.conn.Read(data)if err != nil && err != io.EOF {// 读取超时,发送一个心跳包过去if strings.Contains(err.Error(), "timeout") {// 3秒发一次心跳_ = s.conn.SetReadDeadline(time.Now().Add(time.Second * 3))s.conn.Write([]byte("pi"))continue}fmt.Println("从server读取数据失败, ", err.Error())s.exit <- errreturn}// 如果收到心跳包, 则跳过if data[0] == 'p' && data[1] == 'i' {fmt.Println("client收到心跳包")continue}s.read <- data[:n]}}
}// 将数据写入到Server端
func (s *server) Write(ctx context.Context) {for {select {case <-ctx.Done():returncase data := <-s.write:_, err := s.conn.Write(data)if err != nil && err != io.EOF {s.exit <- errreturn}}}
}type local struct {conn net.Conn// 数据传输通道read  chan []bytewrite chan []byte// 有异常退出通道exit chan error
}func (l *local) Read(ctx context.Context) {for {select {case <-ctx.Done():returndefault:data := make([]byte, 10240)n, err := l.conn.Read(data)if err != nil {l.exit <- errreturn}l.read <- data[:n]}}
}func (l *local) Write(ctx context.Context) {for {select {case <-ctx.Done():returncase data := <-l.write:_, err := l.conn.Write(data)if err != nil {l.exit <- errreturn}}}
}func main() {flag.Parse()target := net.JoinHostPort(host, fmt.Sprintf("%d", remotePort))for {serverConn, err := net.Dial("tcp", target)if err != nil {panic(err)}fmt.Printf("已连接server: %s \n", serverConn.RemoteAddr())server := &server{conn:   serverConn,read:   make(chan []byte),write:  make(chan []byte),exit:   make(chan error),reConn: make(chan bool),}go handle(server)<-server.reConn//_ = server.conn.Close()}}func handle(server *server) {// 等待server端发来的信息,也就是说user来请求server了ctx, cancel := context.WithCancel(context.Background())go server.Read(ctx)go server.Write(ctx)localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort))if err != nil {panic(err)}local := &local{conn:  localConn,read:  make(chan []byte),write: make(chan []byte),exit:  make(chan error),}go local.Read(ctx)go local.Write(ctx)defer func() {_ = server.conn.Close()_ = local.conn.Close()server.reConn <- true}()for {select {case data := <-server.read:local.write <- datacase data := <-local.read:server.write <- datacase err := <-server.exit:fmt.Printf("server have err: %s", err.Error())cancel()returncase err := <-local.exit:fmt.Printf("local have err: %s", err.Error())cancel()return}}
}

server/server.go

package mainimport ("context""flag""fmt""io""net""strings""time"
)var (localPort  intremotePort int
)func init() {flag.IntVar(&localPort, "l", 5200, "the user link port")flag.IntVar(&remotePort, "r", 3333, "client listen port")
}type client struct {conn net.Conn// 数据传输通道read  chan []bytewrite chan []byte// 异常退出通道exit chan error// 重连通道reConn chan bool
}// 从Client端读取数据
func (c *client) Read(ctx context.Context) {// 如果10秒钟内没有消息传输,则Read函数会返回一个timeout的错误_ = c.conn.SetReadDeadline(time.Now().Add(time.Second * 10))for {select {case <-ctx.Done():returndefault:data := make([]byte, 10240)n, err := c.conn.Read(data)if err != nil && err != io.EOF {if strings.Contains(err.Error(), "timeout") {// 设置读取时间为3秒,3秒后若读取不到, 则err会抛出timeout,然后发送心跳_ = c.conn.SetReadDeadline(time.Now().Add(time.Second * 3))c.conn.Write([]byte("pi"))continue}fmt.Println("读取出现错误...")c.exit <- errreturn}// 收到心跳包,则跳过if data[0] == 'p' && data[1] == 'i' {fmt.Println("server收到心跳包")continue}c.read <- data[:n]}}
}// 将数据写入到Client端
func (c *client) Write(ctx context.Context) {for {select {case <-ctx.Done():returncase data := <-c.write:_, err := c.conn.Write(data)if err != nil && err != io.EOF {c.exit <- errreturn}}}
}type user struct {conn net.Conn// 数据传输通道read  chan []bytewrite chan []byte// 异常退出通道exit chan error
}// 从User端读取数据
func (u *user) Read(ctx context.Context) {_ = u.conn.SetReadDeadline(time.Now().Add(time.Second * 200))for {select {case <-ctx.Done():returndefault:data := make([]byte, 10240)n, err := u.conn.Read(data)if err != nil && err != io.EOF {u.exit <- errreturn}u.read <- data[:n]}}
}// 将数据写给User端
func (u *user) Write(ctx context.Context) {for {select {case <-ctx.Done():returncase data := <-u.write:_, err := u.conn.Write(data)if err != nil && err != io.EOF {u.exit <- errreturn}}}
}func main() {flag.Parse()defer func() {err := recover()if err != nil {fmt.Println(err)}}()clientListener, err := net.Listen("tcp", fmt.Sprintf(":%d", remotePort))if err != nil {panic(err)}fmt.Printf("监听:%d端口, 等待client连接... \n", remotePort)// 监听User来连接userListener, err := net.Listen("tcp", fmt.Sprintf(":%d", localPort))if err != nil {panic(err)}fmt.Printf("监听:%d端口, 等待user连接.... \n", localPort)for {// 有Client来连接了clientConn, err := clientListener.Accept()if err != nil {panic(err)}fmt.Printf("有Client连接: %s \n", clientConn.RemoteAddr())client := &client{conn:   clientConn,read:   make(chan []byte),write:  make(chan []byte),exit:   make(chan error),reConn: make(chan bool),}userConnChan := make(chan net.Conn)go AcceptUserConn(userListener, userConnChan)go HandleClient(client, userConnChan)<-client.reConnfmt.Println("重新等待新的client连接..")}
}func HandleClient(client *client, userConnChan chan net.Conn) {ctx, cancel := context.WithCancel(context.Background())go client.Read(ctx)go client.Write(ctx)user := &user{read:  make(chan []byte),write: make(chan []byte),exit:  make(chan error),}defer func() {_ = client.conn.Close()_ = user.conn.Close()client.reConn <- true}()for {select {case userConn := <-userConnChan:user.conn = userConngo handle(ctx, client, user)case err := <-client.exit:fmt.Println("client出现错误, 关闭连接", err.Error())cancel()returncase err := <-user.exit:fmt.Println("user出现错误,关闭连接", err.Error())cancel()return}}
}// 将两个Socket通道链接
// 1. 将从user收到的信息发给client
// 2. 将从client收到信息发给user
func handle(ctx context.Context, client *client, user *user) {go user.Read(ctx)go user.Write(ctx)for {select {case userRecv := <-user.read:// 收到从user发来的信息client.write <- userRecvcase clientRecv := <-client.read:// 收到从client发来的信息user.write <- clientRecvcase <-ctx.Done():return}}
}// 等待user连接
func AcceptUserConn(userListener net.Listener, connChan chan net.Conn) {userConn, err := userListener.Accept()if err != nil {panic(err)}fmt.Printf("user connect: %s \n", userConn.RemoteAddr())connChan <- userConn
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Make New 函数 ---golang
  • 第八篇【传奇开心果系列】Python微项目技术点案例示例:以微项目开发为案例,深度解读Dearpygui 编写图形化界面桌面程序的优势
  • FreeRTOS_事件组_学习笔记
  • Unidac连接Excel文件
  • Stream流的使用
  • Windows批处理命令和概念
  • php部分特性漏洞学习
  • 【设计模式】JAVA Design Patterns——Bridge(桥接模式)
  • 面试被问到不懂的东西,是直接说不懂还是坚持狡辩一下?
  • test_mqtt/python
  • 帝国CMS如何修改时间格式,变成几分钟,几小时教程
  • 常用 CSS 写法
  • 苹果MacOS系统使用微软远程桌面连接Windows电脑桌面详细步骤
  • Mac配置node环境
  • etcd 和 MongoDB 的混沌(故障注入)测试方法
  • 分享一款快速APP功能测试工具
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • JS+CSS实现数字滚动
  • Linux链接文件
  • MobX
  • react 代码优化(一) ——事件处理
  • Webpack入门之遇到的那些坑,系列示例Demo
  • 程序员该如何有效的找工作?
  • 关于 Cirru Editor 存储格式
  • 前端代码风格自动化系列(二)之Commitlint
  • 前端相关框架总和
  • 如何优雅地使用 Sublime Text
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 小程序开发之路(一)
  • 写代码的正确姿势
  • 用简单代码看卷积组块发展
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • raise 与 raise ... from 的区别
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • ​业务双活的数据切换思路设计(下)
  • #HarmonyOS:软件安装window和mac预览Hello World
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (STM32笔记)九、RCC时钟树与时钟 第一部分
  • (紀錄)[ASP.NET MVC][jQuery]-2 純手工打造屬於自己的 jQuery GridView (含完整程式碼下載)...
  • (自适应手机端)行业协会机构网站模板
  • .a文件和.so文件
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .Net Redis的秒杀Dome和异步执行
  • .NET 设计模式初探
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)...
  • .Net8 Blazor 尝鲜
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .Net转前端开发-启航篇,如何定制博客园主题
  • .vue文件怎么使用_vue调试工具vue-devtools的安装