255 lines
5.4 KiB
Go
255 lines
5.4 KiB
Go
package net
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/go-think/openssl"
|
|
"github.com/mitchellh/mapstructure"
|
|
"github.com/gorilla/websocket"
|
|
"log/slog"
|
|
"slgserver/constant"
|
|
"slgserver/util"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// 客户端连接
|
|
type ClientConn struct {
|
|
wsSocket *websocket.Conn // 底层websocket
|
|
isClosed bool
|
|
Seq int64
|
|
onClose func(conn*ClientConn)
|
|
onPush func(conn*ClientConn, body*RspBody)
|
|
//链接属性
|
|
property map[string]interface{}
|
|
//保护链接属性修改的锁
|
|
propertyLock sync.RWMutex
|
|
syncCtxs map[int64]*syncCtx
|
|
syncLock sync.RWMutex
|
|
handshakeChan chan bool
|
|
handshake bool
|
|
}
|
|
|
|
func NewClientConn(wsSocket *websocket.Conn) *ClientConn {
|
|
conn := &ClientConn{
|
|
wsSocket: wsSocket,
|
|
isClosed: false,
|
|
property: make(map[string]interface{}),
|
|
Seq: 0,
|
|
syncCtxs: make(map[int64]*syncCtx),
|
|
handshakeChan: make(chan bool),
|
|
}
|
|
|
|
return conn
|
|
}
|
|
|
|
func (this *ClientConn) waitHandshake() bool{
|
|
if this.handshake == false{
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
select {
|
|
case _ = <-this.handshakeChan:{
|
|
slog.Info("recv handshakeChan")
|
|
return true
|
|
}
|
|
case <-ctx.Done():{
|
|
slog.Info("recv handshakeChan timeout")
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (this *ClientConn) Start() bool{
|
|
this.handshake = false
|
|
go this.wsReadLoop()
|
|
return this.waitHandshake()
|
|
}
|
|
|
|
func (this *ClientConn) Addr() string {
|
|
return this.wsSocket.RemoteAddr().String()
|
|
}
|
|
|
|
func (this *ClientConn) Push(name string, data interface{}) {
|
|
rsp := &WsMsgRsp{Body: &RspBody{Name: name, Msg: data, Seq: 0}}
|
|
this.write(rsp.Body)
|
|
}
|
|
|
|
func (this *ClientConn) Send(name string, data interface{}) *RspBody{
|
|
this.syncLock.Lock()
|
|
sync := newSyncCtx()
|
|
this.Seq += 1
|
|
seq := this.Seq
|
|
req := ReqBody{Name: name, Msg: data, Seq: seq}
|
|
this.syncCtxs[this.Seq] = sync
|
|
this.syncLock.Unlock()
|
|
|
|
rsp := &RspBody{Code: constant.OK, Name: name, Seq: seq }
|
|
err := this.write(req)
|
|
if err != nil{
|
|
sync.cancel()
|
|
}else{
|
|
r := sync.wait()
|
|
if r == nil{
|
|
rsp.Code = constant.ProxyConnectError
|
|
}else{
|
|
rsp = r
|
|
}
|
|
}
|
|
|
|
this.syncLock.Lock()
|
|
delete(this.syncCtxs, seq)
|
|
this.syncLock.Unlock()
|
|
|
|
return rsp
|
|
}
|
|
|
|
func (this *ClientConn) wsReadLoop() {
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
e := fmt.Sprintf("%v", err)
|
|
slog.Error("wsReadLoop error", "err", e)
|
|
this.Close()
|
|
}
|
|
}()
|
|
|
|
for {
|
|
// 读一个message
|
|
_, data, err := this.wsSocket.ReadMessage()
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
data, err = util.UnZip(data)
|
|
if err != nil {
|
|
slog.Error("wsReadLoop UnZip error", "error", err)
|
|
continue
|
|
}
|
|
|
|
//需要检测是否有加密
|
|
body := &RspBody{}
|
|
if secretKey, err := this.GetProperty("secretKey"); err == nil {
|
|
key := secretKey.(string)
|
|
d, err := util.AesCBCDecrypt(data, []byte(key), []byte(key), openssl.ZEROS_PADDING)
|
|
if err != nil {
|
|
slog.Error("AesDecrypt error", "error", err)
|
|
}else{
|
|
data = d
|
|
}
|
|
}
|
|
|
|
if err := util.Unmarshal(data, body); err == nil {
|
|
if body.Seq == 0 {
|
|
if body.Name == HandshakeMsg{
|
|
h := Handshake{}
|
|
mapstructure.Decode(body.Msg, &h)
|
|
slog.Info("client 收到握手协议", "data", string(data))
|
|
if h.Key != ""{
|
|
this.SetProperty("secretKey", h.Key)
|
|
}else{
|
|
this.RemoveProperty("secretKey")
|
|
}
|
|
this.handshake = true
|
|
this.handshakeChan <- true
|
|
}else{
|
|
//推送,需要推送到指定的代理连接
|
|
if this.onPush != nil{
|
|
this.onPush(this, body)
|
|
}else{
|
|
slog.Warn("clientconn not deal push")
|
|
}
|
|
}
|
|
}else{
|
|
this.syncLock.RLock()
|
|
s, ok := this.syncCtxs[body.Seq]
|
|
this.syncLock.RUnlock()
|
|
if ok {
|
|
s.outChan <- body
|
|
}else{
|
|
slog.Warn("seq not found sync",
|
|
"seq", body.Seq,
|
|
"msgName", body.Name)
|
|
}
|
|
}
|
|
|
|
}else{
|
|
slog.Error("wsReadLoop Unmarshal error", "error", err)
|
|
}
|
|
}
|
|
|
|
this.Close()
|
|
}
|
|
|
|
|
|
func (this *ClientConn) write(msg interface{}) error{
|
|
data, err := util.Marshal(msg)
|
|
if err == nil {
|
|
if secretKey, err:= this.GetProperty("secretKey"); err == nil {
|
|
key := secretKey.(string)
|
|
slog.Info("secretKey", "secretKey", key)
|
|
data, _ = util.AesCBCEncrypt(data, []byte(key), []byte(key), openssl.ZEROS_PADDING)
|
|
}
|
|
}else {
|
|
slog.Error("wsWriteLoop Marshal body error", "error", err)
|
|
return err
|
|
}
|
|
|
|
if data, err := util.Zip(data); err == nil{
|
|
if err := this.wsSocket.WriteMessage(websocket.BinaryMessage, data); err != nil {
|
|
this.Close()
|
|
return err
|
|
}
|
|
}else{
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (this *ClientConn) Close() {
|
|
this.wsSocket.Close()
|
|
if !this.isClosed {
|
|
this.isClosed = true
|
|
if this.onClose != nil{
|
|
this.onClose(this)
|
|
}
|
|
}
|
|
}
|
|
|
|
//设置链接属性
|
|
func (this *ClientConn) SetProperty(key string, value interface{}) {
|
|
this.propertyLock.Lock()
|
|
defer this.propertyLock.Unlock()
|
|
|
|
this.property[key] = value
|
|
}
|
|
|
|
//获取链接属性
|
|
func (this *ClientConn) GetProperty(key string) (interface{}, error) {
|
|
this.propertyLock.RLock()
|
|
defer this.propertyLock.RUnlock()
|
|
|
|
if value, ok := this.property[key]; ok {
|
|
return value, nil
|
|
} else {
|
|
return nil, errors.New("no property found")
|
|
}
|
|
}
|
|
|
|
//移除链接属性
|
|
func (this *ClientConn) RemoveProperty(key string) {
|
|
this.propertyLock.Lock()
|
|
defer this.propertyLock.Unlock()
|
|
|
|
delete(this.property, key)
|
|
}
|
|
|
|
func (this *ClientConn) SetOnClose(hookFunc func (*ClientConn)) {
|
|
this.onClose = hookFunc
|
|
}
|
|
|
|
func (this *ClientConn) SetOnPush(hookFunc func (*ClientConn, *RspBody)) {
|
|
this.onPush = hookFunc
|
|
}
|