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