commit e467fa58e856b30830c08d024f187b25dc4d03d9 Author: chenboxing <36821277@qq.com> Date: Sat Jul 22 13:18:33 2017 +0800 First initialize diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7d65a58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.swp +bin/ +pkg/ +src/code.google.com +src/github.com +src/bitbucket.org +src/launchpad.net +src/gopkg.in +src/ngrok/client/assets/ +src/ngrok/server/assets/ diff --git a/src/client.conf b/src/client.conf new file mode 100644 index 0000000..bedefb8 --- /dev/null +++ b/src/client.conf @@ -0,0 +1,7 @@ +[client] +proxy = ":" +listen = ":8585" +key = "" + +[ThirdProxy] +address = "proxy.move8.cn:7777" \ No newline at end of file diff --git a/src/punching/Makefile b/src/punching/Makefile new file mode 100644 index 0000000..a1c0639 --- /dev/null +++ b/src/punching/Makefile @@ -0,0 +1,53 @@ +.PHONY: default server client deps fmt clean all release-all assets client-assets server-assets contributors +export GOPATH:=$(shell pwd) + +BUILDTAGS=debug +default: all + +deps: assets + go get -tags '$(BUILDTAGS)' -d -v ngrok/... + +server: deps + go install -tags '$(BUILDTAGS)' ngrok/main/ngrokd + +fmt: + go fmt ngrok/... + +client: deps + go install -tags '$(BUILDTAGS)' ngrok/main/ngrok + +assets: client-assets server-assets + +bin/go-bindata: + GOOS="" GOARCH="" go get github.com/jteeuwen/go-bindata/go-bindata + +client-assets: bin/go-bindata + bin/go-bindata -nomemcopy -pkg=assets -tags=$(BUILDTAGS) \ + -debug=$(if $(findstring debug,$(BUILDTAGS)),true,false) \ + -o=src/ngrok/client/assets/assets_$(BUILDTAGS).go \ + assets/client/... + +server-assets: bin/go-bindata + bin/go-bindata -nomemcopy -pkg=assets -tags=$(BUILDTAGS) \ + -debug=$(if $(findstring debug,$(BUILDTAGS)),true,false) \ + -o=src/ngrok/server/assets/assets_$(BUILDTAGS).go \ + assets/server/... + +release-client: BUILDTAGS=release +release-client: client + +release-server: BUILDTAGS=release +release-server: server + +release-all: fmt release-client release-server + +all: fmt client server + +clean: + go clean -i -r ngrok/... + rm -rf src/ngrok/client/assets/ src/ngrok/server/assets/ + +contributors: + echo "Contributors to ngrok, both large and small:\n" > CONTRIBUTORS + git log --raw | grep "^Author: " | sort | uniq | cut -d ' ' -f2- | sed 's/^/- /' | cut -d '<' -f1 >> CONTRIBUTORS + diff --git a/src/punching/README.md b/src/punching/README.md new file mode 100644 index 0000000..fed37dc --- /dev/null +++ b/src/punching/README.md @@ -0,0 +1,41 @@ +### 使用场景 + + + + +### 项目介绍 + + 本项目完全是点对点的访问,无须经由服务器转发,代理转发端只是为了获取当自的Nat网络IP和端口,为了Nat穿透,在获取各自对方的IP和端口后,就不需要服务端的干预。 + +### 如何使用 + 1. 迁出源码git clone https://github.com/chenboxing/punching.git + 2. 进入项目目录 src/punching/ + 3. 编译源码 + make (all|server|client|proxy) (windows|darwin|linux|arm) + make all windows #编译windows平台下所有 + make server|client linux #编译Windows平台Server端和Client端二进制文件 + 二进制文件编译在 punching/bin/里 + + 你也可以直接下载已经编译好的文件: + + + 4. 把proxy(代理转发端) 部署到公网计算机上,配置config.conf配置节[proxy],设置侦听端口,默认7777,如果没有公网计算机,可以使用本站设置好的域名 nat.move8.cn, 在config.conf配置节[ThridProxy]设置好相关信息,比如 + [ThirdProxy] + address = nat.move8.cn + email = xxxx@xxxxx.com + password = xxxxxxx + + 先在Nat网络一端,你需要开放访问的服务的计算机上部署server端,配置config.conf配置节[server],在listen项里设置你要开放的应用服务,如 192.168.1.45:80, proxy添写你的代理转发端公网地址和端口,比如 xxx.f3322.net:7777, 如果你需要使用本站提供的代理转发,此项请为空,参考上面填写节[ThirdProxy]信息。 + + 配置好,启动Server端: + nat_server.exe + + 配置在Client端 + 在Nat网络的另一端,部署client端,先配置config.conf,在节[client],需要先设置好要侦听的端口信息,比如listen = :8585, proxy添写你的代理转发端公网地址和端口,比如 xxx.f3322.net:7777, 如果你需要使用本站提供的代理转发,此项请为空,参考上面填写节[ThirdProxy]信息。 + + 配置好,启动Server端: + nat_client.exe + +### 局限 + 只能P2P通讯(Client端,Server端),无法多人访问开启的服务 + \ No newline at end of file diff --git a/src/punching/client/config.go b/src/punching/client/config.go new file mode 100644 index 0000000..69c51c4 --- /dev/null +++ b/src/punching/client/config.go @@ -0,0 +1,62 @@ +package client + +import ( + "fmt" + "punching/util" +) + +/* +[server] +proxy = "" +dial = "" +key = "" + +[ThirdProxy] +address = "proxy.move8.cn:7777" +*/ + +type ServerConfig struct { + Proxy string `toml:"proxy"` // Proxy 服务的地址 + Dial string `toml:"dial"` // 服务端提供的服务地址 + Key string `toml:"key"` // 客户端和服务端的匹配码 +} + +type ThridProxyConfig struct { + Address string `toml:"address"` // Proxy 服务的地址 +} + +var Config *ServerConfig +var ThirdConfig *ThridProxyConfig + +func InitConfig() (err error) { + + if Config == nil { + + // 加载配置信息 + fileName := "server.conf" + sectionName1 := "server" + if err01 := util.DecodeSection(fileName, sectionName1, Config); err01 != nil { + err = fmt.Errorf("Load config file failed, error:%s", err01.Error()) + return + } + + sectionName2 := "ThridProxy" + if err02 := util.DecodeSection(fileName, sectionName2, ThirdConfig); err != nil { + err = fmt.Errorf("Load config file failed, error:%s", err02.Error()) + return + } + + if Config.Key == "" { + err = fmt.Errorf("匹配码不能为空,请在client.conf配置匹配码") + return + } + + if Config.Proxy == "" && ThirdConfig.Address == "" { + err = fmt.Errorf("Proxy服务地址和第三方Proxy服务地址不能同时为空") + return + } + + } + + return nil +} diff --git a/src/punching/client/main.go b/src/punching/client/main.go new file mode 100644 index 0000000..0b03125 --- /dev/null +++ b/src/punching/client/main.go @@ -0,0 +1,50 @@ +package client + +import ( + "fmt" + "log" +) + +func Main() { + + // 加载配置信息 + if err := InitConfig(); err != nil { + fmt.Println("加载配置信息出错,原因为:%s", err) + return + } + + proxyAddr := Config.Dial || ThirdConfig.Address + pairName := Config.Key + localAddr, destAddr, pairname, err := util.ClientDialProxy(proxyAddr, pairName) + + if err != nil { + log.Println(err) + return + } + + //连接对方 + connPeer, errPeer := util.DialPeer(localAddr, destAddr) + if errPeer != nil { //无法连接上 + log.Println(errPeer) + return + } + + // 连接上 P2P客户端 + + Dch = make(chan bool) + Rch = make(chan DataPackage) + Wch = make(chan []byte) + + go RHandler(connPeer) + go WHandler(connPeer) + + ClientListenHandle() +} + +func RHandler(conn util.NetConn) { + +} + +func WHandler(conn util.NetConn) { + +} diff --git a/src/punching/client/transmit.go b/src/punching/client/transmit.go new file mode 100644 index 0000000..94405ca --- /dev/null +++ b/src/punching/client/transmit.go @@ -0,0 +1,175 @@ +package client + +import ( + "crypto/md5" + "crypto/rand" + "encoding/base64" + "encoding/hex" + "fmt" + "io" + "log" + "net" + "os" + "sync" + "time" +) + +var ListenAcceptMap map[string]net.Conn +var ExitChanMap map[string]chan bool + +var RWLock *sync.RWMutex + +//生成32位md5字串 +func GetMd5String(s string) string { + h := md5.New() + h.Write([]byte(s)) + return hex.EncodeToString(h.Sum(nil)) +} + +//生成Guid字串 +func UniqueId() string { + b := make([]byte, 48) + + if _, err := io.ReadFull(rand.Reader, b); err != nil { + return "" + } + return GetMd5String(base64.URLEncoding.EncodeToString(b)) +} + +func handleClientConn(source net.Conn) { + + // 32位唯一码 + uniqueid := UniqueId() + log.Println("Enter handleClientConn:", uniqueid) + + RWLock.Lock() + ListenAcceptMap[uniqueid] = source + ExitChanMap[uniqueid] = make(chan bool) + RWLock.Unlock() + log.Println("建立Map", uniqueid) + + defer func() { + + e1 := source.Close() + if e1 != nil { + log.Println("关闭Sourcer失败") + } + RWLock.Lock() + delete(ListenAcceptMap, uniqueid) + delete(ExitChanMap, uniqueid) + log.Println("删除map", uniqueid) + RWLock.Unlock() + + }() + + go func() { + + buf := make([]byte, 1024) + var flag int + + for { + + len01, err := source.Read(buf) + + if len01 <= 0 || err != nil { + log.Println("读取Source源连接出错,原因为:", err.Error()) + + //发送控制 + pack01 := Packet(uniqueid, "01", []byte("")) + Wch <- pack01 + return + } + + controlID := "00" + if flag == 0 { + // 第一次 + controlID = "11" + flag = 1 + } + pack := Packet(uniqueid, controlID, buf[0:len01]) + Wch <- pack + + } + + }() + + select { + case <-ExitChanMap[uniqueid]: + log.Println("需要退出Accept") + return + } +} + +// 侦听端口,将连接转到natConn +func ClientListenHandle() { + + ListenAcceptMap = make(map[string]net.Conn) + ExitChanMap = make(map[string]chan bool) + RWLock = new(sync.RWMutex) + + addrOn := Config.Dial + l, err := net.Listen("tcp", addrOn) + if err != nil { + fmt.Println("listen ", addrOn, " error:", err) + return + } + fmt.Println("server running at port", addrOn) + + // 全局读取来自nat源的包 + go handleReadConn() + + go func() { + for { + c, err := l.Accept() + if err != nil { + fmt.Println("accept error:", err) + break + } + go handleClientConn(c) + } + }() + + select { + case <-Dch: + os.Exit(3) + } + +} + +// 读取目标流到源 +func handleReadConn() { + for { + select { + case r := <-Rch: + + log.Println(time.Now().UnixNano(), "handleReadConn准备处理") + // 获取src + controlid := r.ControlID + uniqueid := r.Key + data := r.Data + + log.Println("读取Nat包:handleReadConn", uniqueid, "长度为", len(data)) + + //退出 + if controlid == "01" { + if c, ok := ExitChanMap[uniqueid]; ok { + log.Println("发送退出信号") + c <- true + } else { + log.Println("在ExitChanMap里找不到Key为:", uniqueid) + } + } else { + if src, ok := ListenAcceptMap[uniqueid]; ok { + len2, err2 := src.Write(data) + if err2 != nil || len2 <= 0 { + log.Println("源写入出错", err2.Error()) + } + log.Println(time.Now().UnixNano(), "源写入:", len2) + } else { + log.Println("在Map里找不到Key为:", uniqueid) + } + + } + } + } +} diff --git a/src/punching/config.conf b/src/punching/config.conf new file mode 100644 index 0000000..daa6b35 --- /dev/null +++ b/src/punching/config.conf @@ -0,0 +1,17 @@ +[proxy] +listen = ":7777" + +[server] +proxy = ":7777" +dial = "192.168.1.168:443" +key = "amychen" + +[client] +proxy = ":7777" +listen = ":8585" +key = "amychen" + +[ThirdProxy] +address = "proxy.move8.cn:7777" +email = 368123477@qq.com +password = dingding diff --git a/src/punching/const/pair.go b/src/punching/const/pair.go new file mode 100644 index 0000000..a5d3e34 --- /dev/null +++ b/src/punching/const/pair.go @@ -0,0 +1,19 @@ +package const + +// Constant for the client and server + +const ( + PAIR_PACT_HEAD byte = 'P' // C<->S 自定义包头 + PAIR_CONTROL_FIRST byte = 11 // 控制码 C->S第一个包 + PAIR_CONTROL_QUIT byte = 10 // 控制码 退出 + PAIR_CONTROL_NORMAL byte = 0 // 控制码 + + CLIENT_PAIR_ACK byte = 1 // 客户端匹配确认 + SERVER_PAIR_ACK byte = 2 // 服务端匹配确认 +) + +const ( + ROLE_SERVER int = 1 // 点对点服务端 + ROLE_CLIENT int = 2 // 点对点客户端 +} + diff --git a/src/punching/const/proxy.go b/src/punching/const/proxy.go new file mode 100644 index 0000000..0a84971 --- /dev/null +++ b/src/punching/const/proxy.go @@ -0,0 +1,8 @@ +package const +const ( + PROXY_PACKAGE_HEAD byte = 'H' // C<->S 自定义包头 + PROXY_CONTROL_ERROR_NO_SERVER byte = 201 // 服务端还没有注册 + PROXY_CONTROL_ERROR_CLIENT_EXIST byte = 202 // 客户端已经存在 + + +) \ No newline at end of file diff --git a/src/punching/logger/logger.go b/src/punching/logger/logger.go new file mode 100644 index 0000000..eebfb0a --- /dev/null +++ b/src/punching/logger/logger.go @@ -0,0 +1,76 @@ +package util + +import ( + "fmt" + + "github.com/cihub/seelog" +) + +// init 初始化包 +func init() { + // 解析服务配置文件 + xml := ` + + + + + + + + + + + + + ` + + // 解析日志配置(从默认配置) + logger, err := seelog.LoggerFromConfigAsBytes([]byte(xml)) + if err != nil { + panic(fmt.Errorf("log configuration parse error: %s", err.Error())) + } + seelog.ReplaceLogger(logger) + +} + +var ( + // Tracef 写一条格式化的日志信息。级别等于 Trace + Tracef = seelog.Tracef + // Trace 写一条日志信息。级别等于 Trace + Trace = seelog.Trace + + // Debugf 写一条格式化的日志信息。级别等于 Debug + Debugf = seelog.Debugf + + // Debug 写一条日志信息。级别等于 Debug + Debug = seelog.Debug + + // Infof 写一条格式化的日志信息。级别等于 Info + Infof = seelog.Infof + + // Info 写一条日志信息。级别等于 Info + Info = seelog.Info + + // Warnf 写一条格式化的日志信息。级别等于 Warn + Warnf = seelog.Warnf + + // Warn 写一条日志信息。级别等于 Warn + Warn = seelog.Warn + + // Errorf 写一条格式化的日志信息。级别等于 Error + Errorf = seelog.Errorf + + // Error 写一条日志信息。级别等于 Error + Error = seelog.Error + + // Criticalf 写一条格式化的日志信息。级别等于 Critical + Criticalf = seelog.Criticalf + + // Critical 写一条日志信息。级别等于 Critical + Critical = seelog.Critical +) + +// Flush 将所有日志信息写入缓存 +func Flush() { + seelog.Flush() +} diff --git a/src/punching/main/client.go b/src/punching/main/client.go new file mode 100644 index 0000000..88fdbc3 --- /dev/null +++ b/src/punching/main/client.go @@ -0,0 +1,5 @@ +package main + +func main() { + client.Main() +} \ No newline at end of file diff --git a/src/punching/main/proxy.go b/src/punching/main/proxy.go new file mode 100644 index 0000000..9c406a5 --- /dev/null +++ b/src/punching/main/proxy.go @@ -0,0 +1,5 @@ +package main + +func main() { + proxy.Main() +} diff --git a/src/punching/main/server.go b/src/punching/main/server.go new file mode 100644 index 0000000..7a31f8f --- /dev/null +++ b/src/punching/main/server.go @@ -0,0 +1,5 @@ +package main + +func main() { + server.Main() +} diff --git a/src/punching/proxy.conf b/src/punching/proxy.conf new file mode 100644 index 0000000..3a050d6 --- /dev/null +++ b/src/punching/proxy.conf @@ -0,0 +1,2 @@ +[proxy] +listen = ":7777" \ No newline at end of file diff --git a/src/punching/proxy/config.go b/src/punching/proxy/config.go new file mode 100644 index 0000000..2f95d31 --- /dev/null +++ b/src/punching/proxy/config.go @@ -0,0 +1,32 @@ +package proxy + +import ( + "fmt" +) + +type ProxyConfig struct{ + Listen string `toml:"listen"` // Proxy 服务的地址 +} + +var Config *ProxyConfig + +func InitConfig() (err error){ + + if Config == nil { + + // 加载配置信息 + fileName = "proxy.conf" + if err01 := util.DecodeSection(fileName, sectionName, Config); err != nil { + err = fmt.Errorf("Load config file failed, error:%s", err.Error()) + return + } + + if Config.Listen == "" { + err = fmt.Errorf("侦听地址为空,请在配置文件proxy.conf配置listen值") + return + } + + } + + return nil +} \ No newline at end of file diff --git a/src/punching/proxy/main.go b/src/punching/proxy/main.go new file mode 100644 index 0000000..bc04f1d --- /dev/null +++ b/src/punching/proxy/main.go @@ -0,0 +1,343 @@ +package proxy + +import ( + "net" + "sync" + "fmt" + "punching/util" + "punching/logger" + "time" + "punching/const" +) + +const ( + PROXY_FIRST_HEAD byte = 'P' //包头标识 + ROLE_SERVER byte = 1 //服务端 + ROLE_CLIENT byte = 2 //客户端 + PROXY_RESP_NO_SERVER = "no_server_partner" // 代理响应-服务端还没有启用 + PROXY_RESP_CLIENT_EXIST = "client_existed" // 代理响应-客户端已经存在了 + PROXY_RESP_ERR_SERVER_EXIST = "server_existed" // 代理响应-客户端已经存在了 + + CLIENT_RESP_ACK byte = 1 + SERVER_RESP_ACK byte = 1 +) + + +// ServerConn 服务端到代理端连接 +type ServerConn struct { + Rch chan []byte // 读通道 + Wch chan []byte // 写通道 + Dch chan bool // 连接退 + LocalAddr string // 客户端IP信息 + Pairname string // 匹配名称 + SyncAt int64 // 上次心跳时间 +} + +// ClientConn 客户端到代理端连接 +type ClientConn struct{ + Pairname string //匹配码 +} + +// 全局变量 +var ( + OnlineServerList map[string]ServerConn // 服务端连接列表Map + OnlineClientList map[string]ClientConn // 客户端连接列表Map + RWLockClient sync.RWMutex //读写锁 + RWLockServer sync.RWMutex +) + +func Main(){ + + // 加载配置信息 + if err := InitConfig(); err != nil{ + log.Println("加载配置信息出错,原因为:%s", err) + return + } + + + OnlineServerList = make(map[string]ServerConn) + OnlineClientList = make(map[string]ClientConn) + + RWLockClient = new(sync.RWMutex) + RWLockServer = new(sync.RWMutex) + + tcpAddr, err := net.ResolveTCPAddr("tcp", addr) + if err != nil { + panic(err) + } + + listen, err := net.ListenTCP("tcp", tcpAddr) + + if err != nil { + fmt.Println("监听端口失败:", err.Error()) + return + } + fmt.Println("已初始化连接,等待客户端连接...") + + for { + conn, err := listen.AcceptTCP() + if err != nil { + fmt.Println("连接异常:", err.Error()) + continue + } + fmt.Println("本地地址:", conn.LocalAddr().String(), "来自远程地址", conn.RemoteAddr().String()) + go Handler(conn) + } +} + +// parseFirstPackage 解析连接的第一个条 +// [1]+[1]+[4-32] (包头标识+类型+32字节的Pairname) +func parseFirstPackage(data []byte)(pairname string, roleType int, err error){ + if len(data) <= 6 || len(data) > 34 { + err = fmt.Errorf("%s","包长度不匹配") + return + } + + roleType = int(data[1]) + if data[0] != PROXY_FIRST_HEAD || ( roleType != ROLE_CLIENT && roleType != ROLE_SERVER) { + err = fmt.Errorf("%s","包内容不匹配") + return + } + + pairname = string(byte[2:]) + + return +} + +// processRoleClient 处理客户端连接 +func processRoleClient(conn net.Conn,pairname string ){ + + // 判断匹配的服务端是否已经注册 + RWLockServer.RLock() + serverConn,ok := OnlineServerList[pairname] + RWLockServer.RUnlock() + + if !ok { + // 客户端没有注册 + conn.Write([]byte(PROXY_RESP_NO_SERVER)) + return + } + + RWLockClient.RLock() + ok, _ := OnlineClientList[pairname] + RWLockClient.RUnlock() + + if !ok{ + conn.Write([]byte(PROXY_RESP_CLIENT_EXIST)) + return + } + + // 添加到客户端列表 + RWLockClient.Lock() + OnlineClientList[pairname] = pairname + RWLockClient.Unlock() + + // byteNat := bytes.NewBuffer(nil) + // byteNat.write([]byte()) + // result.Bytes() + + + // 发送Nat地址和接收确认 + toClientAddrs := serverConn.LocalAddr + "," + conn.LocalAddr + conn.Write(byte[]{toClientAddrs}) + + buf := make([]byte, 128) + lenAck, err := conn.Read(buf) + + flag := 0 + + if buf[0] == CLIENT_RESP_ACK { + flag += 1 + } + + toServerAddrs := conn.LocalAddr + "," + serverConn.LocalAddr + serverSide.Wch <- toServerAddrs + + // 等待服务端的确认数据 + select { + case bufAck := <- serverSide.Rch + if bufAck[0] == SERVER_RESP_ACK { + flag += 1 + break + } + } + + // 收到服务端的确认数据 + if flag == 2 { + RWLockServer.Lock() + serverConn.Dch <- true // 关闭服务端连接 + delete( OnlineServerList, pairname) + RWLockServer.Unlock() + } + + + RWLockClient.Lock() + delete( OnlineClientList, pairname) + RWLockClient.Unlock() + + return + +} + +// Handle 连接处理函数 +func Handler(conn net.Conn) { + + defer func() { + if r := recover(); r != nil { + logger.Println("连接出现问题:%s",r.Error()) + } + }() + + defer conn.Close() + + buf := make([]byte, 1024) + var uid string + var C *OnlineServerSide + + // 确定连接类型,判断是否是有效的连接, + // 对于客户端,需满足 + // 1. 存在对应的服务端 + // 2. 不能存在多个客户端 + // 对于服务端: + // 1. + + + for { + + i, err := conn.Read(buf) + if err != nil { + fmt.Println("读取数据错误:", err.Error()) + return + } + + //获取匹配名称和连接类型(服务端或客户端) + pairname, roleType, err := parseFirstPackage(buf[0:i]) + if err != nil { + return + } + + // 处理客户端连接 + if roleType == ROLE_CLIENT{ + processRoleClient(conn) + return // 退出客户端连接 + } + + + + + + break + + + } + + + // 下面的操作都是针对服务端连接 + // 是否存在pair name + RWLockServer.RLock() + _, ok := OnlineServerList[pairname] + RWLockServer.RUnlock() + + // 已存在 + if ok { + conn.Write([]byte(PROXY_RESP_ERR_SERVER_EXIST)) + return + } + + // 生成服务器连接对象添加到列表 + RWLockServer.Lock() + serverConn := &ServerConn{Rch: make(chan []byte), Wch: make(chan []byte), Pairname: pairname, LocalAddr: conn.LocalAddr} + OnlineServerList[pairname] = serverConn + RWLockServer.Unlock() + + // 写通道 + go WHandler(conn, serverConn) + + // 读通道 + go RHandler(conn, serverConn) + + // 等待退出通道 + select { + case <-C.Dch: + fmt.Println("close handler goroutine") + } +} + +// 正常写数据 匹配端连接上来会写信息 +// 定时检测 conn die => goroutine die +func WHandler(conn net.Conn, C *ServerConn) { + // 读取业务Work 写入Wch的数据 + ticker := time.NewTicker(60 * time.Second) + for { + select { + case d := <-C.Wch: + conn.Write(d) + case <-ticker.C: //60秒无操作,可能连接已中断 + RWLockServer.RLock() + _, ok := OnlineServerList[C.Pairname]; + RWLockServer.RUnlock() + if !ok { + fmt.Println("conn die, close WHandler") + return + } + } + } +} + +// 读客户端数据 + 心跳检测 +func RHandler(conn net.Conn, C *ServerSide) { + // 心跳ack + // 业务数据 写入Wch + + for { + data := make([]byte, 128) + // 设置读超时 + err := conn.SetReadDeadline(time.Now().Add(10 * time.Second)) + if err != nil { + fmt.Println(err) + } + if _, derr := conn.Read(data); derr == nil { + // 可能是来自客户端的消息确认 + // 数据消息 + fmt.Println(data) + if data[0] == Res { + fmt.Println("recv client data ack") + } else if data[0] == Req { + fmt.Println("recv client data") + fmt.Println(data) + conn.Write([]byte{Res, '#'}) + // C.Rch <- data + } + + continue + } + + //如果等待10秒没有读到客户端数据或读写出错,写心跳包 + + // 写心跳包 + conn.Write([]byte{Req_HEARTBEAT, '#'}) + fmt.Println("send ht packet") + conn.SetReadDeadline(time.Now().Add(5 * time.Second)) + if _, herr := conn.Read(data); herr == nil { + + // fmt.Println(string(data)) + // 更新心跳时间 + RWLockServer.RLock() + serverConn, ok := OnlineServerList[C.Pairname]; + if ok { + serverConn.SyncAt = time.Now().Unix() + } + RWLockServer.RUnlock() + + fmt.Println("resv ht packet ack") + } else { + RWLockServer.Lock() + delete(OnlineServerSide, C.Pairname) + RWLockServer.Unlock() + fmt.Println("delete user!") + return + } + } +} + + diff --git a/src/punching/server.conf b/src/punching/server.conf new file mode 100644 index 0000000..0009188 --- /dev/null +++ b/src/punching/server.conf @@ -0,0 +1,8 @@ +[server] +proxy = "" +dial = "" +key = "" + +[ThirdProxy] +address = "proxy.move8.cn:7777" + diff --git a/src/punching/server/config.go b/src/punching/server/config.go new file mode 100644 index 0000000..44dc301 --- /dev/null +++ b/src/punching/server/config.go @@ -0,0 +1,57 @@ +package server + +import ( + "fmt" + "punching/util" +) + +/* +[server] +proxy = "" +dial = "" +key = "" + +[ThirdProxy] +address = "proxy.move8.cn:7777" +*/ + +type ServerConfig struct { + Proxy string `toml:"proxy"` // Proxy 服务的地址 + Dial string `toml:"dial"` // 服务端提供的服务地址 + Key string `toml:"key"` // 客户端和服务端的匹配码 +} + +type ThridProxyConfig struct { + Address string `toml:"address"` // Proxy 服务的地址 +} + +var Config *ServerConfig +var ThirdConfig *ThridProxyConfig + +func InitConfig() (err error) { + + if Config == nil { + + // 加载配置信息 + fileName := "server.conf" + sectionName1 := "server" + if err01 := util.DecodeSection(fileName, sectionName1, Config); err01 != nil { + err = fmt.Errorf("Load config file failed, error:%s", err01.Error()) + return + } + + sectionName2 := "ThridProxy" + if err02 := util.DecodeSection(fileName, sectionName2, ThirdConfig); err != nil { + err = fmt.Errorf("Load config file failed, error:%s", err02.Error()) + return + } + + if Config.Proxy == "" && ThirdConfig.Address == "" { + err = fmt.Errorf("Proxy服务地址和第三方Proxy服务地址不能同时为空") + return + } + + } + + return nil +} diff --git a/src/punching/server/main.go b/src/punching/server/main.go new file mode 100644 index 0000000..570bb6e --- /dev/null +++ b/src/punching/server/main.go @@ -0,0 +1,50 @@ +package server + +import ( + "fmt" + "log" + "punching/util" + "time" +) + +func Main() { + + // 加载配置信息 + if err := InitConfig(); err != nil { + fmt.Println("加载配置信息出错,原因为:%s", err) + return + } + + proxyAddr := Config.Dial || ThirdConfig.Address + pairName := Config.Key + + // 如果跟Peer连接出错,要重新连接Proxy + for { + + conn, err := ServerDialProxy(proxyAddr, pairName) + + if err != nil { + log.Println(err) + time.Sleep(5 * time.Second) + continue + } + + localAddr, remoteAddr, errWait := WaitForPeer(conn) + if errWait != nil { + log.Println(errWait) + time.Sleep(5 * time.Second) + continue + } + + //连接对方 + connPeer, errPeer := util.DialPeer(localAddr, destAddr) + if errPeer != nil { //无法连接上 + log.Println(errPeer) + continue + } + + //已经连接上 + + } + +} diff --git a/src/punching/server/proxy.go b/src/punching/server/proxy.go new file mode 100644 index 0000000..909ba37 --- /dev/null +++ b/src/punching/server/proxy.go @@ -0,0 +1,117 @@ +package server + +import ( + "fmt" + "log" + "net" + "os" +) + +func WaitForPeer(conn util.NetConn) (localAddr string, remoteAddr string, pairname string, err error) { + + // 接收心跳和客户端连接确认 + Rch = make(chan DataPackage) + Wch = make(chan []byte) + + go RHandler(conn) + go WHandler(conn) + + select { + case ret := <-Dch: + switch ret { + case 10: //无法跟proxy连接 + //关闭连接 + err = error("出现错误") + break + case 11: // 获取客户端发来信息 + localAddr = "" + remoteAddr = "" + pairName = "" + break + + } + return + } +} + +func ServerDialProxy(proxyAddr string, pairkey string) (conn util.NetConn, err error) { + + // 发第一个包 + // 地址接收确认 + // 心跑包接收和确认 + + var conn = util.NetConn{} + + // 不指定端口,让系统自动分配 + err = conn.Bind("tcp", "") + if err != nil { + log.Println("绑定出错", err.Error()) + return + } + + // 连接到Proxy解析服务器 + tcpAddr, err := net.ResolveTCPAddr("tcp", proxyAddr) + err = conn.Connect(util.InetAddr(tcpAddr.IP.String()), tcpAddr.Port) + + if err != nil { + fmt.Println("连接服务端出错", err.Error()) + return + } + defer conn.Close() + fmt.Println("已连接服务器,服务器地址是:%s:%d", tcpAddr.IP.String(), tcpAddr.Port) + + // 发送第一个包 + PackageProxy() + + key := "ok" + name + buff01 := make([]byte, len(key)) + + //发送第一个包 + log.Println("发送数据1", name) + n, err := conn.Write([]byte(name)) + if err != nil { + log.Println("出错,原因:", err.Error()) + os.Exit(22) + } + log.Println("have write: ", n) + + // 读取 + // 错误,退出此次连接 + // 没有错误,显示当前连接的信息 + i, err := conn.Read(buff01) + if err != nil { + fmt.Println("读取数据出错,", err.Error()) + os.Exit(1) + } + + // log.Print("读取数据:", string(buff01[0:i])) + // if string(buff01[0:i]) == key { + // break + // } + // log.Println("发送数据", []byte(name)) + // conn.Write([]byte(name)) + +} + +func RHandler(conn util.NetConn) { + + for { + // 心跳包,回复ack + data := make([]byte, 128) + i, _ := conn.Read(data) + if i == 0 { + Dch <- true + return + } + if data[0] == Req_HEARTBEAT { + fmt.Println("recv ht pack") + conn.Write([]byte{Res_REGISTER, '#', 'h'}) + fmt.Println("send ht pack ack") + } else if data[0] == Req { // 接收到确认信息 + fmt.Println("recv data pack") + fmt.Printf("%v\n", string(data[2:])) + conn.Write([]byte{Res, '#'}) + } + } + +} diff --git a/src/punching/server/service.go b/src/punching/server/service.go new file mode 100644 index 0000000..abb4e43 --- /dev/null +++ b/src/punching/server/service.go @@ -0,0 +1 @@ +package server diff --git a/src/punching/server/transmit.go b/src/punching/server/transmit.go new file mode 100644 index 0000000..192edc1 --- /dev/null +++ b/src/punching/server/transmit.go @@ -0,0 +1,157 @@ +package client + +import ( + "log" + "net" + "os" + "reflect" + "sync" + "unsafe" +) + +var DialTargetMap map[string]net.Conn + +func b2s(buf []byte) string { + return *(*string)(unsafe.Pointer(&buf)) +} + +func s2b(s *string) []byte { + return *(*[]byte)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(s)))) +} + +func handleServerConn() { + + DialTargetMap = make(map[string]net.Conn) + ExitChanMap = make(map[string]chan bool) + RWLock = new(sync.RWMutex) + + //var target net.Conn + //var err error + // defer func() { + // if target != nil { + // target.Close() + // } + // //source.Close() + // }() + + var targetIP string + targetIP = "192.168.3.2:5901" + for { + select { + case r := <-Rch: + + //确定target是否存在,如果不存在,重新生成target + + //分析数据包 + log.Println("接收到数据:", len(r.Data)) + + controlid := r.ControlID + uniqueid := r.Key + pack := r.Data + + //log.Println("读取Nat接收包:handleReadConn", string(r[0:34]), "长度为", len(r)) + + if controlid == "01" { + + RWLock.RLock() + + if c, ok := ExitChanMap[uniqueid]; ok { + log.Println("发送退出信号") + c <- true + } else { + log.Println("在ExitChanMap里找不到Key为:", uniqueid) + } + RWLock.RUnlock() + break + } + + //第一次 + if controlid == "11" { + log.Println("准备连接:", targetIP) + target, err := net.Dial("tcp", targetIP) + if err != nil { + log.Println("连接目标出错", targetIP) + break + } + + ExitChanMap[uniqueid] = make(chan bool) + DialTargetMap[uniqueid] = target + + log.Println("连接目标成功:", targetIP) + + _, err2 := target.Write(pack) + if err2 != nil { + log.Println("连接成功后写目标出错", err2.Error()) + break + } + go ReadFromTarget(target, uniqueid) + } else { + + if dialtarget, ok := DialTargetMap[uniqueid]; ok { + + len2, err2 := dialtarget.Write(pack) + log.Println("已写入:", len2) + if err2 != nil { + log.Println("写目标出错", targetIP, err2.Error()) + //发送控制 + pack01 := Packet(uniqueid, "01", []byte("")) + Wch <- pack01 + + break + } + + } else { + log.Println("找不到目标Dial:") + } + + } + + case <-Dch: + //出错 + os.Exit(3) + } + } +} + +// 读取目标流到源 +func ReadFromTarget(target net.Conn, uniqueid string) { + defer func() { + target.Close() + + RWLock.Lock() + delete(DialTargetMap, uniqueid) + delete(ExitChanMap, uniqueid) + RWLock.Unlock() + }() + + go func() { + //buf := make([]byte, 512-34) + buf := make([]byte, 1024) + for { + + j, err := target.Read(buf) + + if err != nil || j == 0 { + log.Println("读取目标连接数据出错,原因为:", err.Error()) + + pack := Packet(uniqueid, "01", []byte("")) + Wch <- pack + + return + } + + pack := Packet(uniqueid, "00", buf[0:j]) + + Wch <- pack + + } + }() + + //接受到退出标识 + select { + case <-ExitChanMap[uniqueid]: + log.Println("需要退出Accept") + return + } + +} diff --git a/src/punching/util/configuration.go b/src/punching/util/configuration.go new file mode 100644 index 0000000..1824aa6 --- /dev/null +++ b/src/punching/util/configuration.go @@ -0,0 +1,48 @@ +package util + +import ( + "flag" + "fmt" + "os" + + "github.com/BurntSushi/toml" +) + + +// LoadTomlFile 加载配置文件 +func LoadTomlFile(filename string)(sections map[string]toml.Primitive, meta MetaData err error){ + // 判断配置文件是否存在 + if _, err := os.Stat(fileName); err != nil { + if os.IsNotExist(err) { + err = fmt.Errorf("configuration file %s does not exist.\r\n", fileName) + } else { + err = fmt.Errorf("configuration file %s execption:%s\r\n", fileName, err.Error()) + } + return + } + + // 加载配置文件 + var sections map[string]toml.Primitive + + if meta, err = toml.DecodeFile(fileName, &file); err != nil { + err = fmt.Errorf("load configuration file %s failed:%s", fileName, err.Error()) + }else{ + err = meta.PrimitiveDecode(file, §ions) + } + return +} + +// DecodeSection 解码一个节点的配置信息 +func DecodeSection(filename, name string, v interface{}) (err error) { + + sections, _err := loadTomlFile(filename) + if err != nil{ + return + } + + if section, ok := sections[name]; ok { + return meta.PrimitiveDecode(section, v) + } + + return nil +} diff --git a/src/punching/util/conv.go b/src/punching/util/conv.go new file mode 100644 index 0000000..9d1e96f --- /dev/null +++ b/src/punching/util/conv.go @@ -0,0 +1,45 @@ +package util + +import ( + "bytes" + "encoding/binary" + "strconv" + "strings" +) + +func InetAddr(ipaddr string) [4]byte { + var ( + ips = strings.Split(ipaddr, ".") + ip [4]uint64 + ret [4]byte + ) + for i := 0; i < 4; i++ { + ip[i], _ = strconv.ParseUint(ips[i], 10, 8) + } + for i := 0; i < 4; i++ { + ret[i] = byte(ip[i]) + } + return ret +} + +func InetPort(ipport string) uint16 { + ret, _ := strconv.ParseUint(ipport, 10, 16) + return uint16(ret) +} + +// IntToBytes 整形转换成字节 +func IntToBytes(n int) []byte { + x := int32(n) + + bytesBuffer := bytes.NewBuffer([]byte{}) + binary.Write(bytesBuffer, binary.BigEndian, x) + return bytesBuffer.Bytes() +} + +// BytesToInt 字节转换成整形 +func BytesToInt(b []byte) int { + bytesBuffer := bytes.NewBuffer(b) + var x int32 + binary.Read(bytesBuffer, binary.BigEndian, &x) + return int(x) +} diff --git a/src/punching/util/net.go b/src/punching/util/net.go new file mode 100644 index 0000000..64cda29 --- /dev/null +++ b/src/punching/util/net.go @@ -0,0 +1,137 @@ +package util + +import ( + "fmt" + "log" + "net" + "os" + "p2p/util" + "strings" + "time" +) + +// 连接到Proxy代理解析端, 地址/类型/IP +// 发送第一个包->(接收到错误数据,退出,否则接收到NatAddress,->确认收到 <->完成 不断接收心跳包,确认) +// 客户端 错误失败 | nat地址 +// 服务端 错误失败 | 注册 心跑包 +func ClientDialProxy(proxyAddr string, pairkey string, role string) (localAddr string, destAddr string, pairname string, err error) { + + // 发第一个包 + // 地址接收确认 + // 心跑包接收和确认 + + var conn = util.NetConn{} + + // 不指定端口,让系统自动分配 + err = conn.Bind("tcp", "") + if err != nil { + log.Println("绑定出错", err.Error()) + return + } + + // 连接到Proxy解析服务器 + tcpAddr, err := net.ResolveTCPAddr("tcp", proxyAddr) + err = conn.Connect(util.InetAddr(tcpAddr.IP.String()), tcpAddr.Port) + + if err != nil { + fmt.Println("连接服务端出错", err.Error()) + return + } + defer conn.Close() + fmt.Println("已连接服务器,服务器地址是:%s:%d", tcpAddr.IP.String(), tcpAddr.Port) + + for { + + PackageProxy() + + key := "ok" + name + buff01 := make([]byte, len(key)) + + //发送第一个包 + log.Println("发送数据1", name) + n, err := conn.Write([]byte(name)) + if err != nil { + log.Println("出错,原因:", err.Error()) + os.Exit(22) + } + log.Println("have write: ", n) + + i, err := conn.Read(buff01) + if err != nil { + fmt.Println("读取数据出错,", err.Error()) + os.Exit(1) + } + log.Print("读取数据:", string(buff01[0:i])) + if string(buff01[0:i]) == key { + break + } + log.Println("发送数据", []byte(name)) + conn.Write([]byte(name)) + } + + fmt.Println("--- Request sent, waiting for parkner in Name ...", name) + + buff := make([]byte, 512) + + //获取服务发送过来的对方IP信息 + len, err := conn.Read(buff) + if err != nil { + log.Println("客户端读取数据出错:", err.Error()) + os.Exit(1) + } + + pairAddr := string(buff[0:len]) + arrPairAddr := strings.Split(pairAddr, ",") + remoteAddrStr := arrPairAddr[0] + localAddrStr := arrPairAddr[1] + log.Println("读取到远程IP", remoteAddrStr) + + conn.Close() + //time.Sleep(3 * time.Second) + + remoteAddr, err := net.ResolveTCPAddr("tcp", remoteAddrStr) + + //localAddr.String() + log.Println("local addr:", localAddrStr) + +} + + + +func DialPeer(localAddr string, remoteAddr string) util.NetConn { + + remoteTCPAddr, err := net.ResolveTCPAddr("tcp", remoteAddr) + + var conn util.NetConn + + // 不指定端口,让系统自动分配 + err = conn.Bind("tcp", localAddr) + if err != nil { + log.Println("绑定出错", err.Error()) + return + } + + // TODO 应该封装成一个函数 + // 有时连接并不成功,多次连接 + log.Println("远程地址是:", remoteAddr.IP.String(), remoteAddr.Port) + + tryCount := 0 + for { + tryCount += 1 + + if tryCount > 10 { + log.Printf("连接不上,退出") + return + } + err02 := conn02.Connect(util.InetAddr(remoteAddr.IP.String()), remoteAddr.Port) + if err02 != nil { + log.Printf("第%d次不能连接远程服务器:%s", tryCount, err02.Error()) + time.Sleep(1 * time.Second) + continue + } else { + log.Println("已经连接到peer: ", remoteAddr.String()) + break + } + } + +} diff --git a/src/punching/util/netconn_darwin.go b/src/punching/util/netconn_darwin.go new file mode 100644 index 0000000..6a4c814 --- /dev/null +++ b/src/punching/util/netconn_darwin.go @@ -0,0 +1,125 @@ +package util + +import ( + "fmt" + "log" + "net" + "os" + "syscall" + "time" +) + +type NetConn struct { + fd int // 文件句柄 + conn *net.Conn // 连接对象 +} + +func (hole *NetConn) Close() { + if hole.conn != nil { + hole.conn.Close() + } + +} + +func (hole *NetConn) Bind(proto, addr string) (err error) { + + if "tcp" != proto { + log.Println("tcp != proto") + return + } + + syscall.ForkLock.RLock() + if fd, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); err != nil { + syscall.ForkLock.RUnlock() + return + } + syscall.ForkLock.RUnlock() + + defer func() { + if err != nil { + syscall.Close(fd) + } + }() + + if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { + return + } + + if len(addr) > 0 { + var tcp *net.TCPAddr + tcp, err = net.ResolveTCPAddr(proto, addr) + if err != nil && tcp.IP != nil { + log.Println(err) + return + } + sockaddr := &syscall.SockaddrInet4{Port: tcp.Port} + if err = syscall.Bind(fd, sockaddr); err != nil { + return + } + } + + hole.fd = fd + + return +} + +func (hole *NetConn) Connect(addr [4]byte, port int) (err error) { + + if hole.fd == 0 { + err = error{"请先调用Bind()函数"} + return + } + + addrInet4 := syscall.SockaddrInet4{ + Addr: addr, + Port: port, + } + + chConnect := make(chan error) + go func() { + err = syscall.Connect(fd, &addrInet4) + chConnect <- err + }() + + //有时候连接被远端抛弃的时候, syscall.Connect() 会很久才返回 + ticker := time.NewTicker(60 * time.Second) + select { + case <-ticker.C: + err = fmt.Errorf("Connect timeout") + return + case e := <-chConnect: + if e != nil { + err = e + log.Println("Connect error: ", err) + return + } + } + + // 转为net.conn对象 + var file *os.File + file = os.NewFile(uintptr(fd), fmt.Sprintf("tcpholepunching.%d", time.Now().UnixNano())) + if conn0, err0 := net.FileConn(file); err0 != nil { + log.Println("Connect error", err0) + err = err0 + return + } else { + hole.conn = &conn0 + } + + if err = file.Close(); err != nil { + log.Println("Connect error", err) + return + } + return + +} + +func (hole *NetConn) Read(buffer []byte) (length int, err error) { + + return hole.Read(buffer) +} + +func (hole *NetConn) Write(data []byte) (length int, err error) { + + return hole.Write(data) +} diff --git a/src/punching/util/netconn_windows.go b/src/punching/util/netconn_windows.go new file mode 100644 index 0000000..bc56f4a --- /dev/null +++ b/src/punching/util/netconn_windows.go @@ -0,0 +1,131 @@ +package util + +import ( + "fmt" + "log" + "net" + "syscall" + "time" +) + +type NetConn struct { + sock syscall.Handle +} + +func (hole *NetConn) Close() { + + syscall.WSACleanup() + syscall.Closesocket(hole.sock) + +} + +func (hole *NetConn) Bind(proto, addr string) (err error) { + + if "tcp" != proto { + + log.Println("tcp != proto") + return + } + + var wsadata syscall.WSAData + + if err = syscall.WSAStartup(MAKEWORD(2, 2), &wsadata); err != nil { + log.Println("Startup error") + return + } + + var sock syscall.Handle + syscall.ForkLock.RLock() + if sock, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); err != nil { + syscall.ForkLock.RUnlock() + return + } + syscall.ForkLock.RUnlock() + + defer func() { + if err != nil { + syscall.Close(sock) + } + }() + + if err = syscall.SetsockoptInt(sock, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { + return + } + + if len(addr) > 0 { + var tcp *net.TCPAddr + tcp, err = net.ResolveTCPAddr(proto, addr) + if err != nil && tcp.IP != nil { + log.Println(err) + return + } + sockaddr := &syscall.SockaddrInet4{Port: tcp.Port} + if err = syscall.Bind(sock, sockaddr); err != nil { + return + } + } + + hole.sock = sock + return +} + +func (hole *NetConn) Connect(addr [4]byte, port int) (err error) { + if hole.sock == 0 { + err = Error{"请先执行Bind()"} + return + } + addrInet4 := syscall.SockaddrInet4{ + Addr: addr, + Port: port, + } + + chConnect := make(chan error) + go func() { + err = syscall.Connect(hole.sock, &addrInet4) + chConnect <- err + }() + + //有时候连接被远端抛弃的时候, syscall.Connect() 会很久才返回 + ticker := time.NewTicker(30 * time.Second) + select { + case <-ticker.C: + err = fmt.Errorf("Connect timeout") + return + case e := <-chConnect: + if e != nil { + err = e + log.Println("Connect error: ", err) + return + } + } + return nil +} + +func (hole *NetConn) Read(buffer []byte) (length int, err error) { + + dataWsaBuf := syscall.WSABuf{Len: uint32(len(buffer)), Buf: &buffer[0]} + flags := uint32(0) + recvd := uint32(0) + + err = syscall.WSARecv(hole.sock, &dataWsaBuf, 1, &recvd, &flags, nil, nil) + if err != nil { + return 0, err + } + return int(recvd), nil +} + +func (hole *NetConn) Write(data []byte) (length int, err error) { + var ( + dataWsaBuf syscall.WSABuf + SendBytes uint32 + overlapped syscall.Overlapped + ) + dataWsaBuf.Len = uint32(len(data)) + dataWsaBuf.Buf = &data[0] + err = syscall.WSASend(hole.sock, &dataWsaBuf, 1, &SendBytes, 0, &overlapped, nil) + if err != nil { + return 0, err + } else { + return int(SendBytes), nil + } +} diff --git a/src/punching/util/package.go b/src/punching/util/package.go new file mode 100644 index 0000000..3120f1a --- /dev/null +++ b/src/punching/util/package.go @@ -0,0 +1,67 @@ +package util + +const { + PACKAGE_PROXY_HEADER byte = 'P' + PACKAGE_NAT_HEADER [6]byte = []byte{'C','B','X','N','A','T'} +} + +// 跟代理解析端通讯封包 +func PackageProxy(control byte, data []byte) []byte{ + pack := bytes.NewBuffer(nil) + pack.Write(PACKAGE_PROXY_HEADER) + pack.Write(control) + pack.Write(data) + return pack.Bytes() +} + +// 跟代理解析端通讯拆包 +func UnpackageProxy(pact []byte)(control byte, data []byte) { + +} + +// Nat网络后面的Client端和Server端封包 +func PackageNat(control byte, sessionKey [4]byte, data []byte) []byte{ + +} + +// Nat网络后面的Client端和Server端拆包 +// 需要考虑沾包,分析出的完整封装包传入读channel +func UnpackageNat(pact []byte)(data []byte){ + + length := len(buffer) + log.Println("长度为:", length) + var i int + for i = 0; i < length; i = i + 1 { + if length < i+ConstHeaderLength+ConstControlLength+ConstDataLength { + break + } + if string(buffer[i:i+len(ConstHeaderKey)]) == ConstHeaderKey { + // 获取头数据长度 + messageLength := BytesToInt(buffer[i+ConstHeaderLength+ConstControlLength : i+ConstHeaderLength+ConstControlLength+ConstDataLength]) + + log.Println("数据长度为:", messageLength) + log.Println("需要的包长度:", ConstHeaderLength+ConstControlLength+ConstDataLength+messageLength) + if length < i+ConstHeaderLength+ConstControlLength+ConstDataLength+messageLength { + break + } + data := buffer[i+ConstHeaderLength+ConstControlLength+ConstDataLength : i+ConstHeaderLength+ConstControlLength+ConstDataLength+messageLength] + controlID := string(buffer[i+ConstHeaderLength : i+ConstHeaderLength+ConstControlLength]) + key := string(buffer[i+len(ConstHeaderKey) : i+ConstHeaderLength]) + log.Println("控制ID为:", controlID, "key为", key) + dataPackage := DataPackage{ + Data: data, + ControlID: controlID, + Key: key, + } + Rch <- dataPackage + + i += ConstHeaderLength + ConstControlLength + ConstDataLength + messageLength - 1 + } + } + + if i == length { + return make([]byte, 0) + } + return buffer[i:] + +} \ No newline at end of file diff --git a/src/punching/util/rand.go b/src/punching/util/rand.go new file mode 100644 index 0000000..2b56348 --- /dev/null +++ b/src/punching/util/rand.go @@ -0,0 +1,66 @@ +package util + +import ( + "crypto/md5" + "crypto/rand" + "encoding/base64" + "encoding/hex" + "io" + "time" +) + +//生成32位md5字串 +func GetMd5String(s string) string { + h := md5.New() + h.Write([]byte(s)) + return hex.EncodeToString(h.Sum(nil)) +} + +//生成Guid字串 +func UniqueId() string { + b := make([]byte, 48) + + if _, err := io.ReadFull(rand.Reader, b); err != nil { + return "" + } + return GetMd5String(base64.URLEncoding.EncodeToString(b)) +} + +// GenerateRandomPairKey 获取4位随机匹配码 +func GenerateRandomPairKey() string { + //97~122 小写字母 + rndNums := GenerateRandomNumber(97, 122, 4) + key := "" + for num, _ := range rndNums { + key = key + string(byte{num}) + } + return key +} + +//生成count个[start,end)结束的不重复的随机数 +func GenerateRandomNumber(start int, end int, count int) []int { + //范围检查 + if end < start || (end-start) < count { + return nil + } + //存放结果的slice + nums := make([]int, 0) + //随机数生成器,加入时间戳保证每次生成的随机数不一样 + r := rand.New(rand.NewSource(time.Now().UnixNano())) + for len(nums) < count { + //生成随机数 + num := r.Intn((end - start)) + start + //查重 + exist := false + for _, v := range nums { + if v == num { + exist = true + break + } + } + if !exist { + nums = append(nums, num) + } + } + return nums +}