当前位置:   article > 正文

IM即时通讯-核心结构体设计(四)_im 通讯表设计

im 通讯表设计

消息结构体 Message

  1. type Message struct {
  2. MsgId int64 `json:"msg_id,omitempty" form:"msg_id" bson:"msg_id"` // 消息ID
  3. OwnerId string `json:"owner_id,omitempty" form:"owner_id" bson:"owner_id"` // 己方,发出者
  4. OtherId string `json:"other_id,omitempty" form:"other_id" bson:"other_id"` // 对方,接收者
  5. MsgType int `json:"msg_type,omitempty" form:"msg_type" bson:"msg_type"` // 消息类型
  6. Media int `json:"media,omitempty" form:"media" bson:"media"` // 消息按什么样式展示
  7. Content string `json:"content,omitempty" form:"content" bson:"content"` // 消息内容
  8. Pic string `json:"pic,omitempty" form:"pic" bson:"pic"` // 预览图片
  9. Url string `json:"url,omitempty" form:"url" bson:"url"` // 图片地址
  10. Desc string `json:"desc,omitempty" form:"desc" bson:"desc"` // 描述内容
  11. CreateTime int64 `json:"create_time" form:"create_time" bson:"create_time"` // 消息时间
  12. }

其中消息类型,以及消息的展示形式如下

  1. const (
  2. // type 消息类型
  3. MSG_TYPE_HEART = 1 // 心跳消息
  4. MSG_TYPE_SINGLE = 2 // 单聊消息
  5. MSG_TYPE_ROOM = 3 // 群聊消息
  6. MSG_TYPE_ACK = 4 // 应答消息
  7. Msg_TYPE_OTHER = 4 // 其他业务消息
  8. // media 消息展示样式
  9. MSG_MEDIA_TEXT = 1 // 文本
  10. MSG_MEDIA_IMAGE = 2 // 图片
  11. MSG_MEDIA_FILE = 3 // 文件
  12. )

客户端结构体 Client

  1. type Client struct {
  2. Uid string // 用户id
  3. Manager *Manager // 对应管理者
  4. Conn *websocket.Conn // websocket 连接
  5. Send chan []byte // 待发送出去的消息
  6. }
  7. // ReadPump 等待用户发来消息,并将消息转交到Manager,由Manager负责发给对方或存储为离线消息
  8. func (c *Client) ReadPump() {
  9. defer func() {
  10. c.Manager.Unregister <- c
  11. c.Conn.Close()
  12. }()
  13. c.Conn.SetReadLimit(maxMessageSize)
  14. c.Conn.SetReadDeadline(time.Now().Add(pongWait))
  15. c.Conn.SetPongHandler(func(string) error { c.Conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
  16. for {
  17. _, message, err := c.Conn.ReadMessage()
  18. if err != nil {
  19. if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
  20. log.Printf("error: %v", err)
  21. }
  22. break
  23. }
  24. message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
  25. c.Manager.MsgBuff <- message
  26. }
  27. }
  28. // WritePump 待发送消息通道收到消息,直接将消息发送给客户端
  29. func (c *Client) WritePump() {
  30. // 定时向客户端发送ping消息,属于一种心跳保活机制
  31. // 活跃用户基数庞大的IM中,一般由客户端发起,减小服务端压力
  32. ticker := time.NewTicker(pingPeriod)
  33. defer func() {
  34. ticker.Stop()
  35. c.Manager.Unregister <- c
  36. c.Conn.Close()
  37. }()
  38. for {
  39. select {
  40. case message, ok := <-c.Send:
  41. c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
  42. if !ok {
  43. c.Conn.WriteMessage(websocket.CloseMessage, []byte{})
  44. return
  45. }
  46. w, err := c.Conn.NextWriter(websocket.TextMessage)
  47. if err != nil {
  48. return
  49. }
  50. w.Write(message)
  51. // Add queued chat messages to the current websocket message
  52. n := len(c.Send)
  53. for i := 0; i < n; i++ {
  54. w.Write(newline)
  55. w.Write(<-c.Send)
  56. }
  57. if err := w.Close(); err != nil {
  58. return
  59. }
  60. case <-ticker.C:
  61. c.Conn.SetWriteDeadline(time.Now().Add(writeWait))
  62. if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
  63. return
  64. }
  65. }
  66. }
  67. }

管理者结构体 Manager

  1. // 管理员,负责管理client
  2. type Manager struct {
  3. Clients map[string]*Client // 客户端列表
  4. Register chan *Client // 注册器,当有新连接时,注册到客户端列表
  5. Unregister chan *Client // 注销器,发现有连接断开时,将其从客户端列表中移除
  6. MsgBuff chan []byte // 入站消息队列
  7. }
  8. // Run 启动管理
  9. func (m *Manager) Run() {
  10. for {
  11. select {
  12. case client := <-m.Register: // 注册
  13. m.Clients[client.Uid] = client
  14. case client := <-m.Unregister: // 注销
  15. if _, ok := m.Clients[client.Uid]; ok {
  16. delete(m.Clients, client.Uid)
  17. close(client.Send)
  18. }
  19. case msg := <-m.MsgBuff:
  20. var message model.Message
  21. if err := json.Unmarshal(msg, &message); err != nil {
  22. fmt.Println("消息解析失败:", string(msg))
  23. break
  24. }
  25. // 对方在线,或者对方离线
  26. if client, ok := m.Clients[message.OtherId]; ok {
  27. client.Send <- msg
  28. } else {
  29. fmt.Println("other down")
  30. }
  31. }
  32. }
  33. }

工作流

用户上线后,就会与服务端连接,产生一个Client,并给到Manager统一管理;

  1. func WsHandler(c *gin.Context, m *service.Manager) {
  2. uid := c.Query("uid")
  3. conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
  4. if err != nil {
  5. http.NotFound(c.Writer, c.Request)
  6. return
  7. }
  8. client := &service.Client{
  9. Uid: uid,
  10. Manager: m,
  11. Conn: conn,
  12. Send: make(chan []byte, 256),
  13. }
  14. fmt.Println("有用户上线:", uid)
  15. // 将client注册到Manager
  16. client.Manager.Register <- client
  17. // new goroutines.
  18. go client.WritePump()
  19. go client.ReadPump()
  20. }

用户发送消息,Client收到客户端发来的消息,将消息转交给Manager(c.Manager.MsgBuff <- message),再由Manager找到对方Client,将消息发出去

  1. // 对方在线,或者对方离线
  2. if client, ok := m.Clients[message.OtherId]; ok {
  3. client.Send <- msg
  4. } else {
  5. fmt.Println("other down")
  6. }

Message、Client、Manager之间的关系这里就讲清楚了,对片段代码不是很理解,这个项目结束会将项目代码放到码云,请关注后续。

IM即时通讯从0到1的实践,相关文章:

IM即时通讯-从0到1的实践(一)

IM即时通讯-项目框架搭建(二)

IM即时通讯-用户注册登录,及gin+JWT鉴权(三)

IM即时通讯-核心结构体设计(四)​​​​​​​<本文>

IM即时通讯-消息id(五)

IM即时通讯-会话列表和会话信箱(六)

IM即时通讯-1.0版成果展示与后续扩展(七)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/849739
推荐阅读
相关标签
  

闽ICP备14008679号