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") } } // WriteMessage 向客户端发送消息 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) } } // WriteBytes 向客户端发送字节数据 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() }