mirror of
https://github.com/chenboxing/punching.git
synced 2025-12-24 13:17:57 +08:00
First initialize
This commit is contained in:
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -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/
|
||||
7
src/client.conf
Normal file
7
src/client.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
[client]
|
||||
proxy = ":"
|
||||
listen = ":8585"
|
||||
key = ""
|
||||
|
||||
[ThirdProxy]
|
||||
address = "proxy.move8.cn:7777"
|
||||
53
src/punching/Makefile
Normal file
53
src/punching/Makefile
Normal file
@@ -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
|
||||
|
||||
41
src/punching/README.md
Normal file
41
src/punching/README.md
Normal file
@@ -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端),无法多人访问开启的服务
|
||||
|
||||
62
src/punching/client/config.go
Normal file
62
src/punching/client/config.go
Normal file
@@ -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
|
||||
}
|
||||
50
src/punching/client/main.go
Normal file
50
src/punching/client/main.go
Normal file
@@ -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) {
|
||||
|
||||
}
|
||||
175
src/punching/client/transmit.go
Normal file
175
src/punching/client/transmit.go
Normal file
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/punching/config.conf
Normal file
17
src/punching/config.conf
Normal file
@@ -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
|
||||
19
src/punching/const/pair.go
Normal file
19
src/punching/const/pair.go
Normal file
@@ -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 // 点对点客户端
|
||||
}
|
||||
|
||||
8
src/punching/const/proxy.go
Normal file
8
src/punching/const/proxy.go
Normal file
@@ -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 // 客户端已经存在
|
||||
|
||||
|
||||
)
|
||||
76
src/punching/logger/logger.go
Normal file
76
src/punching/logger/logger.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cihub/seelog"
|
||||
)
|
||||
|
||||
// init 初始化包
|
||||
func init() {
|
||||
// 解析服务配置文件
|
||||
xml := `
|
||||
<seelog>
|
||||
<outputs formatid="main">
|
||||
<filter levels="info,critical,error,debug">
|
||||
<console formatid="main" />
|
||||
<rollingfile formatid="main" type="date" filename="./log/punching.log" datepattern="2006.01.02" />
|
||||
</filter>
|
||||
</outputs>
|
||||
|
||||
<formats>
|
||||
<format id="main" format="%Date %Time [%LEV] %Msg [%File][%FuncShort][%Line]%n"/>
|
||||
</formats>
|
||||
</seelog>
|
||||
`
|
||||
|
||||
// 解析日志配置(从默认配置)
|
||||
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()
|
||||
}
|
||||
5
src/punching/main/client.go
Normal file
5
src/punching/main/client.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
client.Main()
|
||||
}
|
||||
5
src/punching/main/proxy.go
Normal file
5
src/punching/main/proxy.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
proxy.Main()
|
||||
}
|
||||
5
src/punching/main/server.go
Normal file
5
src/punching/main/server.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
server.Main()
|
||||
}
|
||||
2
src/punching/proxy.conf
Normal file
2
src/punching/proxy.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
[proxy]
|
||||
listen = ":7777"
|
||||
32
src/punching/proxy/config.go
Normal file
32
src/punching/proxy/config.go
Normal file
@@ -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
|
||||
}
|
||||
343
src/punching/proxy/main.go
Normal file
343
src/punching/proxy/main.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
src/punching/server.conf
Normal file
8
src/punching/server.conf
Normal file
@@ -0,0 +1,8 @@
|
||||
[server]
|
||||
proxy = ""
|
||||
dial = ""
|
||||
key = ""
|
||||
|
||||
[ThirdProxy]
|
||||
address = "proxy.move8.cn:7777"
|
||||
|
||||
57
src/punching/server/config.go
Normal file
57
src/punching/server/config.go
Normal file
@@ -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
|
||||
}
|
||||
50
src/punching/server/main.go
Normal file
50
src/punching/server/main.go
Normal file
@@ -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
|
||||
}
|
||||
|
||||
//已经连接上
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
117
src/punching/server/proxy.go
Normal file
117
src/punching/server/proxy.go
Normal file
@@ -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, '#'})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
1
src/punching/server/service.go
Normal file
1
src/punching/server/service.go
Normal file
@@ -0,0 +1 @@
|
||||
package server
|
||||
157
src/punching/server/transmit.go
Normal file
157
src/punching/server/transmit.go
Normal file
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
48
src/punching/util/configuration.go
Normal file
48
src/punching/util/configuration.go
Normal file
@@ -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
|
||||
}
|
||||
45
src/punching/util/conv.go
Normal file
45
src/punching/util/conv.go
Normal file
@@ -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)
|
||||
}
|
||||
137
src/punching/util/net.go
Normal file
137
src/punching/util/net.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
125
src/punching/util/netconn_darwin.go
Normal file
125
src/punching/util/netconn_darwin.go
Normal file
@@ -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)
|
||||
}
|
||||
131
src/punching/util/netconn_windows.go
Normal file
131
src/punching/util/netconn_windows.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
67
src/punching/util/package.go
Normal file
67
src/punching/util/package.go
Normal file
@@ -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:]
|
||||
|
||||
}
|
||||
66
src/punching/util/rand.go
Normal file
66
src/punching/util/rand.go
Normal file
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user