Files
slgserver/net/serverconn.go
2025-11-18 18:08:48 +08:00

288 lines
6.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package net
import (
"errors"
"fmt"
"log/slog"
"slgserver/util"
"sync"
"time"
"github.com/go-think/openssl"
"github.com/mitchellh/mapstructure"
"github.com/gorilla/websocket"
)
// 客户端连接
type ServerConn struct {
wsSocket *websocket.Conn // 底层websocket
outChan chan *WsMsgRsp // 写队列
isClosed bool
needSecret bool
Seq int64
router *Router
beforeClose func(conn WSConn)
onClose func(conn WSConn)
//链接属性
property map[string]interface{}
//保护链接属性修改的锁
propertyLock sync.RWMutex
closeChan chan struct{}
closeOnce sync.Once
}
func NewServerConn(wsSocket *websocket.Conn, needSecret bool) *ServerConn {
conn := &ServerConn{
wsSocket: wsSocket,
outChan: make(chan *WsMsgRsp, 1000),
isClosed: false,
property: make(map[string]interface{}),
needSecret: needSecret,
Seq: 0,
closeChan: make(chan struct{}),
}
return conn
}
// 开启异步
func (this *ServerConn) Start() {
go this.wsReadLoop()
go this.wsWriteLoop()
}
func (this *ServerConn) Addr() string {
return this.wsSocket.RemoteAddr().String()
}
func (this *ServerConn) Push(name string, data interface{}) {
rsp := &WsMsgRsp{Body: &RspBody{Name: name, Msg: data, Seq: 0}}
this.enqueue(rsp)
}
func (this *ServerConn) Send(name string, data interface{}) {
this.Seq += 1
rsp := &WsMsgRsp{Body: &RspBody{Name: name, Msg: data, Seq: this.Seq}}
this.enqueue(rsp)
}
func (this *ServerConn) wsReadLoop() {
defer func() {
if err := recover(); err != nil {
e := fmt.Sprintf("%v", err)
slog.Error("wsReadLoop error", "err", e)
this.Close()
}
}()
for {
select {
case <-this.closeChan:
return
default:
}
// 读一个message
_, data, err := this.wsSocket.ReadMessage()
slog.Debug("ws read message", "size", len(data))
if err != nil {
break
}
data, err = util.UnZip(data)
if err != nil {
slog.Error("wsReadLoop UnZip error", "error", err)
continue
}
body := &ReqBody{}
if this.needSecret {
//检测是否有加密没有加密发起Handshake
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)
this.Handshake()
} else {
data = d
}
} else {
slog.Info("secretKey not found client need handshake", "error", err)
this.Handshake()
return
}
}
if err := util.Unmarshal(data, body); err == nil {
req := &WsMsgReq{Conn: this, Body: body}
rsp := &WsMsgRsp{Body: &RspBody{Name: body.Name, Seq: req.Body.Seq}}
if req.Body.Name == HeartbeatMsg {
h := &Heartbeat{}
mapstructure.Decode(body.Msg, h)
h.STime = time.Now().UnixNano() / 1e6
rsp.Body.Msg = h
} else {
if this.router != nil {
this.router.Run(req, rsp)
}
}
this.outChan <- rsp
} else {
slog.Error("wsReadLoop Unmarshal error", "error", err)
this.Handshake()
}
}
this.Close()
}
func (this *ServerConn) wsWriteLoop() {
defer func() {
if err := recover(); err != nil {
slog.Error("wsWriteLoop error")
this.Close()
}
}()
for {
select {
case <-this.closeChan:
return
case msg := <-this.outChan:
if msg == nil {
continue
}
// 写给websocket
this.write(msg.Body)
}
}
}
func (this *ServerConn) write(msg interface{}) error {
data, err := util.Marshal(msg)
slog.Debug("ws write message", "size", len(data))
if err == nil {
if this.needSecret {
if secretKey, err := this.GetProperty("secretKey"); err == nil {
key := secretKey.(string)
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 *ServerConn) Close() {
this.closeOnce.Do(func() {
this.wsSocket.Close()
close(this.closeChan)
if !this.isClosed {
this.isClosed = true
if this.beforeClose != nil {
this.beforeClose(this)
}
if this.onClose != nil {
this.onClose(this)
}
}
})
}
// 设置链接属性
func (this *ServerConn) SetProperty(key string, value interface{}) {
this.propertyLock.Lock()
defer this.propertyLock.Unlock()
this.property[key] = value
}
// 获取链接属性
func (this *ServerConn) 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 *ServerConn) SetRouter(router *Router) {
this.router = router
}
func (this *ServerConn) SetOnClose(hookFunc func(WSConn)) {
this.onClose = hookFunc
}
func (this *ServerConn) SetOnBeforeClose(hookFunc func(WSConn)) {
this.beforeClose = hookFunc
}
// 移除链接属性
func (this *ServerConn) RemoveProperty(key string) {
this.propertyLock.Lock()
defer this.propertyLock.Unlock()
delete(this.property, key)
}
// 握手协议
func (this *ServerConn) Handshake() {
secretKey := ""
if this.needSecret {
key, err := this.GetProperty("secretKey")
if err == nil {
secretKey = key.(string)
} else {
secretKey = util.RandSeq(16)
}
}
handshake := &Handshake{Key: secretKey}
body := &RspBody{Name: HandshakeMsg, Msg: handshake}
if data, err := util.Marshal(body); err == nil {
if secretKey != "" {
this.SetProperty("secretKey", secretKey)
} else {
this.RemoveProperty("secretKey")
}
slog.Info("handshake secretKey",
"secretKey", secretKey)
if data, err = util.Zip(data); err == nil {
this.wsSocket.WriteMessage(websocket.BinaryMessage, data)
}
} else {
slog.Error("handshake Marshal body error", "error", err)
}
}
func (this *ServerConn) enqueue(rsp *WsMsgRsp) {
select {
case <-this.closeChan:
return
case this.outChan <- rsp:
}
}