2025-05-25 20:02:15 +08:00
|
|
|
package ws
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2025-06-02 15:29:03 +08:00
|
|
|
"github.com/fox/fox/ipb"
|
2025-05-25 20:02:15 +08:00
|
|
|
"github.com/fox/fox/log"
|
2025-06-02 15:29:03 +08:00
|
|
|
"github.com/fox/fox/safeChan"
|
|
|
|
"github.com/golang/protobuf/proto"
|
2025-05-25 20:02:15 +08:00
|
|
|
"github.com/gorilla/websocket"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// 连接id
|
|
|
|
var (
|
|
|
|
wsMsgType = websocket.BinaryMessage
|
|
|
|
nextConnId uint32
|
|
|
|
)
|
|
|
|
|
|
|
|
// 客户端读写消息
|
|
|
|
type wsMessage struct {
|
|
|
|
messageType int
|
|
|
|
data []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// 客户端连接
|
|
|
|
type wsConnect struct {
|
2025-06-02 15:29:03 +08:00
|
|
|
wsConn *websocket.Conn // 底层websocket
|
|
|
|
inChan *safeChan.SafeChan[*wsMessage] // 读队列
|
|
|
|
outChan *safeChan.SafeChan[*wsMessage] // 写队列
|
2025-05-25 20:02:15 +08:00
|
|
|
id uint32
|
|
|
|
userId int64
|
|
|
|
onDisconnect func(IConn)
|
2025-06-02 15:29:03 +08:00
|
|
|
once sync.Once
|
2025-06-17 18:24:33 +08:00
|
|
|
connMgr *connManager
|
2025-05-25 20:02:15 +08:00
|
|
|
}
|
|
|
|
|
2025-06-17 18:24:33 +08:00
|
|
|
func newWsConnect(wsConn *websocket.Conn, onDisconnect func(IConn), connMgr *connManager) *wsConnect {
|
2025-06-02 15:29:03 +08:00
|
|
|
c := &wsConnect{
|
2025-05-25 20:02:15 +08:00
|
|
|
wsConn: wsConn,
|
2025-06-02 15:29:03 +08:00
|
|
|
inChan: safeChan.NewSafeChan[*wsMessage](1000),
|
|
|
|
outChan: safeChan.NewSafeChan[*wsMessage](1000),
|
2025-05-25 20:02:15 +08:00
|
|
|
id: nextConnId,
|
|
|
|
userId: 0,
|
|
|
|
onDisconnect: onDisconnect,
|
2025-06-17 18:24:33 +08:00
|
|
|
connMgr: connMgr,
|
2025-05-25 20:02:15 +08:00
|
|
|
}
|
2025-06-02 15:29:03 +08:00
|
|
|
return c
|
2025-05-25 20:02:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 把消息放进写队列
|
|
|
|
func (c *wsConnect) SendMsg(data []byte) error {
|
2025-06-17 18:24:33 +08:00
|
|
|
if c == nil {
|
|
|
|
return fmt.Errorf("wsConnect is nil")
|
|
|
|
}
|
2025-06-02 15:29:03 +08:00
|
|
|
return c.outChan.Write(&wsMessage{messageType: wsMsgType, data: data})
|
2025-05-28 14:54:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 关闭链接
|
2025-06-02 15:29:03 +08:00
|
|
|
func (c *wsConnect) Close() {
|
2025-06-17 18:24:33 +08:00
|
|
|
if c == nil {
|
|
|
|
return
|
|
|
|
}
|
2025-06-02 15:29:03 +08:00
|
|
|
c.once.Do(func() {
|
2025-06-02 16:51:07 +08:00
|
|
|
//log.Debug(c.Log("关闭链接"))
|
2025-06-02 15:29:03 +08:00
|
|
|
c.inChan.Close()
|
|
|
|
c.outChan.Close()
|
|
|
|
_ = c.wsConn.Close()
|
2025-05-25 20:02:15 +08:00
|
|
|
if c.onDisconnect != nil {
|
|
|
|
c.onDisconnect(c)
|
|
|
|
}
|
2025-06-17 18:24:33 +08:00
|
|
|
c.connMgr.Remove(c)
|
2025-06-02 15:29:03 +08:00
|
|
|
})
|
2025-05-25 20:02:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 循环从websocket中读取消息放入到读队列中
|
|
|
|
func (c *wsConnect) readWsLoop() {
|
2025-06-02 16:51:07 +08:00
|
|
|
defer func() {
|
|
|
|
//log.Debug(c.Log("readWsLoop协程退出"))
|
|
|
|
c.Close()
|
|
|
|
}()
|
|
|
|
|
2025-06-01 10:33:41 +08:00
|
|
|
c.wsConn.SetReadLimit(maxMessageSize)
|
2025-05-25 20:02:15 +08:00
|
|
|
_ = c.wsConn.SetReadDeadline(time.Now().Add(pongWait))
|
2025-06-01 10:33:41 +08:00
|
|
|
c.wsConn.SetPongHandler(func(string) error {
|
2025-06-02 16:51:07 +08:00
|
|
|
//log.Debug(c.Log("received pong. from client"))
|
2025-06-01 10:33:41 +08:00
|
|
|
_ = c.wsConn.SetReadDeadline(time.Now().Add(pongWait))
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
2025-05-25 20:02:15 +08:00
|
|
|
for {
|
|
|
|
// 读一个message
|
|
|
|
msgType, data, err := c.wsConn.ReadMessage()
|
|
|
|
if err != nil {
|
|
|
|
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure, websocket.CloseNormalClosure) {
|
|
|
|
log.Error(c.Log("消息读取出现错误:%v", err))
|
|
|
|
}
|
2025-06-02 16:51:07 +08:00
|
|
|
//log.Debug(c.Log("关闭连接:%v", err))
|
2025-05-25 20:02:15 +08:00
|
|
|
return
|
|
|
|
}
|
2025-06-02 19:18:48 +08:00
|
|
|
if msgType != wsMsgType {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
msg := &wsMessage{messageType: msgType, data: data}
|
|
|
|
if err = c.inChan.Write(msg); err != nil {
|
|
|
|
log.Error(c.Log("读队列关闭:%v", err))
|
2025-06-02 16:51:07 +08:00
|
|
|
return
|
2025-05-25 20:02:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *wsConnect) writeWsLoop() {
|
|
|
|
ticker := time.NewTicker(pingPeriod)
|
2025-06-02 16:51:07 +08:00
|
|
|
defer func() {
|
|
|
|
//log.Debug(c.Log("writeWsLoop协程退出"))
|
|
|
|
ticker.Stop()
|
|
|
|
// 关闭连接
|
|
|
|
c.Close()
|
|
|
|
}()
|
2025-05-25 20:02:15 +08:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
// 取一个消息发送给客户端
|
2025-06-02 16:51:07 +08:00
|
|
|
case msg, ok := <-c.outChan.Reader():
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
2025-05-25 20:02:15 +08:00
|
|
|
if err := c.wsConn.WriteMessage(msg.messageType, msg.data); err != nil {
|
2025-06-02 15:29:03 +08:00
|
|
|
iMsg := &ipb.InternalMsg{}
|
|
|
|
_ = proto.Unmarshal(msg.data, iMsg)
|
|
|
|
log.Error(c.Log("发送消息错误:%v 消息长度:%v 消息内容:%v", err, len(msg.data), iMsg))
|
2025-05-25 20:02:15 +08:00
|
|
|
return
|
|
|
|
}
|
2025-06-02 16:51:07 +08:00
|
|
|
|
2025-05-25 20:02:15 +08:00
|
|
|
case <-ticker.C:
|
2025-06-02 15:29:03 +08:00
|
|
|
if err := c.wsConn.WriteMessage(websocket.PingMessage, []byte("ping")); err != nil {
|
|
|
|
log.Error(c.Log("发送心跳失败:%v", err))
|
2025-05-25 20:02:15 +08:00
|
|
|
return
|
2025-06-02 15:29:03 +08:00
|
|
|
} else {
|
2025-06-02 16:51:07 +08:00
|
|
|
//log.Debug(c.Log("发送心跳"))
|
2025-05-25 20:02:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *wsConnect) handle(process func(IConn, []byte)) {
|
2025-06-02 16:51:07 +08:00
|
|
|
defer c.Close()
|
2025-05-25 20:02:15 +08:00
|
|
|
for {
|
2025-06-02 15:29:03 +08:00
|
|
|
select {
|
|
|
|
case msg, ok := <-c.inChan.Reader():
|
2025-06-02 16:51:07 +08:00
|
|
|
if !ok {
|
|
|
|
//log.Debug(c.Log("handle协程退出"))
|
|
|
|
return
|
2025-06-02 15:29:03 +08:00
|
|
|
}
|
2025-06-02 16:51:07 +08:00
|
|
|
process(c, msg.data)
|
2025-05-25 20:02:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 设置用户id
|
|
|
|
func (c *wsConnect) setUserId(uid int64) {
|
|
|
|
c.userId = uid
|
|
|
|
}
|
|
|
|
|
|
|
|
// 获取连接id
|
|
|
|
func (c *wsConnect) Id() uint32 {
|
|
|
|
return c.id
|
|
|
|
}
|
|
|
|
|
|
|
|
// 获取用户id
|
|
|
|
func (c *wsConnect) UserId() int64 {
|
|
|
|
return c.userId
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *wsConnect) Name() string {
|
|
|
|
return fmt.Sprintf("用户:%v, 地址:%v", c.userId, c.wsConn.RemoteAddr())
|
|
|
|
}
|
|
|
|
|
2025-05-29 20:30:18 +08:00
|
|
|
func (c *wsConnect) Addr() string {
|
|
|
|
return c.wsConn.RemoteAddr().String()
|
|
|
|
}
|
|
|
|
|
2025-05-25 20:02:15 +08:00
|
|
|
func (c *wsConnect) Log(format string, v ...interface{}) string {
|
2025-06-02 15:29:03 +08:00
|
|
|
s := fmt.Sprintf("连接:%v, id:%v uid:%v", c.wsConn.RemoteAddr().String(), c.id, c.userId)
|
2025-05-25 20:02:15 +08:00
|
|
|
return s + fmt.Sprintf(format, v...)
|
|
|
|
}
|