140 lines
2.7 KiB
Go
140 lines
2.7 KiB
Go
package ws_handler
|
||
|
||
import (
|
||
"common/log"
|
||
"common/net/socket"
|
||
"common/proto/gen/cs"
|
||
"context"
|
||
"fmt"
|
||
"go.uber.org/zap"
|
||
"google.golang.org/protobuf/proto"
|
||
"runtime/debug"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
var GatewaySID int64
|
||
|
||
type Client struct {
|
||
sync.WaitGroup
|
||
conn socket.ISocketConn // Socket
|
||
mailChan chan Event // 邮箱队列
|
||
logger *zap.SugaredLogger // 日志
|
||
ctx context.Context // 上下文
|
||
cancel context.CancelFunc // 取消上下文
|
||
heartBeat time.Time // 最后一次心跳
|
||
|
||
UID int
|
||
SceneSID int64 // 场景服ID
|
||
InstanceID int
|
||
UniqueNo int64
|
||
}
|
||
|
||
func NewClient(uid int, conn socket.ISocketConn) *Client {
|
||
client := &Client{
|
||
UID: uid,
|
||
conn: conn,
|
||
logger: log.GetLogger().Named(fmt.Sprintf("uid:%v", uid)),
|
||
heartBeat: time.Now(),
|
||
mailChan: make(chan Event, 1024),
|
||
}
|
||
client.ctx, client.cancel = context.WithCancel(context.Background())
|
||
client.Add(1)
|
||
go client.Loop()
|
||
return client
|
||
}
|
||
|
||
func (c *Client) Loop() {
|
||
defer func() {
|
||
if err := recover(); err != nil {
|
||
c.logger.Errorf("Client Loop err: %v", err)
|
||
debug.PrintStack()
|
||
}
|
||
}()
|
||
defer c.onClose()
|
||
t := time.NewTicker(20 * time.Second)
|
||
defer t.Stop()
|
||
for {
|
||
select {
|
||
case <-c.ctx.Done():
|
||
return
|
||
case evt, ok := <-c.mailChan:
|
||
if ok {
|
||
c.handle(evt)
|
||
}
|
||
case <-t.C:
|
||
_ = c.conn.Ping()
|
||
if time.Now().Sub(c.heartBeat) > 120*time.Second {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func (c *Client) OnEvent(event Event) {
|
||
defer func() {
|
||
if err := recover(); err != nil {
|
||
c.logger.Warnf(fmt.Sprintf("send event chan error: %v", err))
|
||
}
|
||
}()
|
||
select {
|
||
case c.mailChan <- event:
|
||
default:
|
||
c.logger.Warnf("Client mailChan full")
|
||
}
|
||
}
|
||
|
||
func (c *Client) WriteMessage(id cs.MessageID, data proto.Message) {
|
||
d, err := proto.Marshal(data)
|
||
if err != nil {
|
||
c.logger.Errorf("WriteMessage proto.Marshal err: %v", err)
|
||
return
|
||
}
|
||
m, err := proto.Marshal(&cs.Message{
|
||
ID: id,
|
||
Payload: d,
|
||
})
|
||
if err != nil {
|
||
c.logger.Errorf("WriteMessage proto.Marshal err: %v", err)
|
||
return
|
||
}
|
||
if err = c.conn.Write(m); err != nil {
|
||
c.logger.Errorf("WriteMessage err: %v", err)
|
||
}
|
||
}
|
||
|
||
func (c *Client) WriteBytes(id cs.MessageID, data []byte) {
|
||
m, err := proto.Marshal(&cs.Message{
|
||
ID: id,
|
||
Payload: data,
|
||
})
|
||
if err != nil {
|
||
c.logger.Errorf("WriteBytes proto.Marshal err: %v", err)
|
||
return
|
||
}
|
||
if err = c.conn.Write(m); err != nil {
|
||
c.logger.Errorf("WriteBytes err: %v", err)
|
||
}
|
||
}
|
||
|
||
// CloseClient 关闭客户端(同步,会等待onClose执行完成)
|
||
func (c *Client) CloseClient() {
|
||
if c.cancel != nil {
|
||
c.cancel()
|
||
c.Wait()
|
||
}
|
||
}
|
||
|
||
func (c *Client) onClose() {
|
||
if c.conn != nil {
|
||
_ = c.conn.Close()
|
||
c.conn = nil
|
||
}
|
||
if c.mailChan != nil {
|
||
close(c.mailChan)
|
||
c.mailChan = nil
|
||
}
|
||
UserMgr.Delete(c.UID)
|
||
c.Done()
|
||
}
|