client.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. package websocket
  2. import (
  3. "encoding/json"
  4. "log"
  5. "time"
  6. "github.com/gorilla/websocket"
  7. )
  8. const (
  9. // 客户端发送 ping 的最大等待时间
  10. writeWait = 10 * time.Second
  11. // 从客户端读取 pong 的最大等待时间
  12. pongWait = 60 * time.Second
  13. // 发送 ping 的频率(必须小于 pongWait)
  14. pingPeriod = (pongWait * 9) / 10
  15. // 最大消息大小
  16. maxMessageSize = 512 * 1024 // 512KB
  17. )
  18. // Client 是一个 WebSocket 客户端
  19. type Client struct {
  20. hub *Hub
  21. // WebSocket 连接
  22. conn *websocket.Conn
  23. // 发送消息的通道
  24. send chan *Message
  25. // 对话ID(这个客户端属于哪个对话)
  26. conversationID uint
  27. // 是否是访客(true 表示访客,false 表示客服)
  28. isVisitor bool
  29. // 客服ID(如果是客服连接,存储客服的用户ID)
  30. agentID uint
  31. }
  32. // NewClient 创建一个新的客户端
  33. func NewClient(hub *Hub, conn *websocket.Conn, conversationID uint, isVisitor bool, agentID uint) *Client {
  34. return &Client{
  35. hub: hub,
  36. conn: conn,
  37. send: make(chan *Message, 256),
  38. conversationID: conversationID,
  39. isVisitor: isVisitor,
  40. agentID: agentID,
  41. }
  42. }
  43. // ReadPump 从 WebSocket 连接读取消息
  44. func (c *Client) ReadPump() {
  45. defer func() {
  46. c.hub.unregister <- c
  47. c.conn.Close()
  48. }()
  49. // 设置读取限制和超时
  50. c.conn.SetReadDeadline(time.Now().Add(pongWait))
  51. c.conn.SetReadLimit(maxMessageSize)
  52. c.conn.SetPongHandler(func(string) error {
  53. c.conn.SetReadDeadline(time.Now().Add(pongWait))
  54. return nil
  55. })
  56. // 持续读取消息
  57. for {
  58. _, _, err := c.conn.ReadMessage()
  59. if err != nil {
  60. if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
  61. log.Printf("⚠️ WebSocket 读取错误: 对话ID=%d, 错误=%v", c.conversationID, err)
  62. }
  63. break
  64. }
  65. // 目前我们不需要处理客户端发送的消息,只接收心跳包
  66. // 如果需要双向通信,可以在这里处理客户端消息
  67. }
  68. }
  69. // WritePump 向 WebSocket 连接写入消息
  70. func (c *Client) WritePump() {
  71. ticker := time.NewTicker(pingPeriod)
  72. defer func() {
  73. ticker.Stop()
  74. c.conn.Close()
  75. }()
  76. for {
  77. select {
  78. case message, ok := <-c.send:
  79. c.conn.SetWriteDeadline(time.Now().Add(writeWait))
  80. if !ok {
  81. // Hub 关闭了通道
  82. c.conn.WriteMessage(websocket.CloseMessage, []byte{})
  83. return
  84. }
  85. // 发送消息
  86. if err := c.conn.WriteJSON(message); err != nil {
  87. log.Printf("❌ WebSocket 写入错误: 对话ID=%d, 类型=%s, 错误=%v",
  88. c.conversationID, message.Type, err)
  89. return
  90. }
  91. case <-ticker.C:
  92. // 定期发送 ping 保持连接
  93. c.conn.SetWriteDeadline(time.Now().Add(writeWait))
  94. if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
  95. log.Printf("❌ 发送 ping 失败: 对话ID=%d, 错误=%v", c.conversationID, err)
  96. return
  97. }
  98. }
  99. }
  100. }
  101. // SendMessage 发送消息给客户端(用于测试)
  102. func (c *Client) SendMessage(messageType string, data interface{}) error {
  103. message := &Message{
  104. ConversationID: c.conversationID,
  105. Type: messageType,
  106. Data: data,
  107. }
  108. messageJSON, err := json.Marshal(message)
  109. if err != nil {
  110. return err
  111. }
  112. c.conn.SetWriteDeadline(time.Now().Add(writeWait))
  113. return c.conn.WriteMessage(websocket.TextMessage, messageJSON)
  114. }