几行代码使用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 main

import (
	"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 mode
	gin.SetMode(gin.DebugMode)

	// 修改默认路由打印格式
	gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
		log.Printf("【%v】====> %v\n", httpMethod, absolutePath)
	}

	// 全局中间件,阻止panic
	r.Use(gin.Recovery())

	// 为某个group下的路由注册中间件
	r.Group("/admin").Use(func(context *gin.Context) {
		if id := context.GetHeader("adminUserId"); id == "" {
			context.JSON(http.StatusForbidden, "请登录管理员")
		}
	})

	// 开启pprof
	pprof.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 {
		// 字段不能为空,并且不等于  admin
		return value != "" && "admin" != value
	}
	return true
}

type Req struct {
	Name string `validate:"NotNullAndAdmin"`
}

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

package main

import "github.com/koding/kite"

func main() {
	// Create a kite
	k := 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 square
		return result, nil // send back the result
	}).DisableAuthentication()

	// Attach to a server with port 3636 and run it
	k.Config.Port = 3636
	k.Run()
}

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

client/client.go

package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"net"
	"strings"
	"time"
)

var (
	host       string
	localPort  int
	remotePort 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 []byte
	write 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():
			return
		default:
			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 <- err
				return
			}

			// 如果收到心跳包, 则跳过
			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():
			return
		case data := <-s.write:
			_, err := s.conn.Write(data)
			if err != nil && err != io.EOF {
				s.exit <- err
				return
			}
		}
	}
}

type local struct {
	conn net.Conn
	// 数据传输通道
	read  chan []byte
	write chan []byte
	// 有异常退出通道
	exit chan error
}

func (l *local) Read(ctx context.Context) {

	for {
		select {
		case <-ctx.Done():
			return
		default:
			data := make([]byte, 10240)
			n, err := l.conn.Read(data)
			if err != nil {
				l.exit <- err
				return
			}
			l.read <- data[:n]
		}
	}
}

func (l *local) Write(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case data := <-l.write:
			_, err := l.conn.Write(data)
			if err != nil {
				l.exit <- err
				return
			}
		}
	}
}

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 <- data

		case data := <-local.read:
			server.write <- data

		case err := <-server.exit:
			fmt.Printf("server have err: %s", err.Error())
			cancel()
			return
		case err := <-local.exit:
			fmt.Printf("local have err: %s", err.Error())
			cancel()
			return
		}
	}
}

server/server.go

package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"net"
	"strings"
	"time"
)

var (
	localPort  int
	remotePort 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 []byte
	write 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():
			return
		default:
			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 <- err
				return
			}

			// 收到心跳包,则跳过
			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():
			return
		case data := <-c.write:
			_, err := c.conn.Write(data)
			if err != nil && err != io.EOF {
				c.exit <- err
				return
			}
		}
	}
}

type user struct {
	conn net.Conn
	// 数据传输通道
	read  chan []byte
	write 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():
			return
		default:
			data := make([]byte, 10240)
			n, err := u.conn.Read(data)
			if err != nil && err != io.EOF {
				u.exit <- err
				return
			}
			u.read <- data[:n]
		}
	}
}

// 将数据写给User端
func (u *user) Write(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case data := <-u.write:
			_, err := u.conn.Write(data)
			if err != nil && err != io.EOF {
				u.exit <- err
				return
			}
		}
	}
}

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.reConn
		fmt.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 = userConn
			go handle(ctx, client, user)
		case err := <-client.exit:
			fmt.Println("client出现错误, 关闭连接", err.Error())
			cancel()
			return
		case 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 <- userRecv
		case clientRecv := <-client.read:
			// 收到从client发来的信息
			user.write <- clientRecv

		case <-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
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码讲故事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值