ToB企服应用市场:ToB评测及商务社交产业平台

标题: [go-websocket 搭建一对一,一对多的聊天室] 第二篇:websocket间的通信 [打印本页]

作者: 海哥    时间: 2022-8-29 00:08
标题: [go-websocket 搭建一对一,一对多的聊天室] 第二篇:websocket间的通信
源码地址
https://gitee.com/bin-0821/chat-room-demo-go-websocket
关于websocket,上一篇文章讲述了如何通过websocket进行服务端与客户端的通信,本篇将会带领大家把各个websocket进行相互通信,在开始之前,请确保有理解
1 go的通道
2 go的线程
3 gin基础
事实上,websocket与websocket之间是无法进行直接相互通信的,需要我们将数据接收后,发送给另一个websocket链接,可以理解为
  1. conn1.ReadJson(&data)
  2. conn2.WriteJson(data)
复制代码
而建立一个类似微信聊天一样的,能进行多群聊,一对多,一对一的聊天,需要对websocket进行管理,本篇文章的重点便是如何管理好所有用户的websocket连接,主要有以下方面
1,一个根据业务进行设计的数据结构
2,用户上下线后conn的处理
3,用户发送信息的群发或单发
首先,要搞清楚我们在做什么,聊天室要实现的功能类似微信
a,b,c三人,1,2,3 三个聊天室
  1. 人员        加入的聊天室
  2. a        1,2,3
  3. b        1,2
  4. c        1
复制代码
a在1发送信息,全部人都能收到
a在2发送信息,c收不到,以此类推
a可以与c单独发送信息,接收方不在线时,系统能正常运行
1. 目录结构

相比上篇文章 多了manage_socket_conn,service两个模块,重点在于多了manage_socket_conn模块中
  1. D:.
  2. │  go.mod
  3. │  go.sum
  4. │  main.go
  5. │  msg.json
  6. ├─api
  7. │      socket_conn.go
  8. ├─manage_socket_conn        //用户的websocket管理模块
  9. │      char_room_thread.go        //线程 主要负责对信息的群发
  10. │      room_set.go        //聊天室房间管理,房间的创建,销毁 存储房间内的用户id
  11. │      user_set.go        //用户websocket链接管理,信息的发送,存储所有在线的webscoket链接,用户上下线
  12. ├─middleware
  13. │      Cros.go
  14. ├─model
  15. │      socket_msg_form_front.go
  16. │      to_front.go
  17. ├─route
  18. │      route.go
  19. └─service
  20.         chat_room.go        //数据层,模拟用户加入了那些聊天室
复制代码
2. 代码内容

user_set.go
  1. package manage_socket_conn
  2. import (
  3.         "WebSocketDemo/model"
  4.         "errors"
  5.         "fmt"
  6.         "github.com/gorilla/websocket"
  7.         "sync"
  8. )
  9. func init() {
  10.         GetUserSet()
  11. }
  12. //用户map 用来存储每个在线的用户id与对应的conn
  13. type userSet struct {
  14.         //        用户链接集  用户id => 链接对象
  15.         users map[int]*websocket.Conn
  16.         lock  sync.Mutex
  17.         once  sync.Once
  18. }
  19. var us = new(userSet)
  20. //        单例模式
  21. func GetUserSet() *userSet {
  22.         us.once.Do(func() {
  23.                 us.users = make(map[int]*websocket.Conn)
  24.                 us.users[-1] = nil
  25.                 us.lock = sync.Mutex{}
  26.         })
  27.         return us
  28. }
  29. //        用户创建发起websocket连接
  30. // join_type 加入模式
  31. //                1 正常加入 占线无法加入
  32. //                2 强制加入 即踢下线前者
  33. func (u *userSet) ConnConnect(user_id, join_type int, conn *websocket.Conn) (int, error) {
  34.         u.lock.Lock()
  35.         defer u.lock.Unlock()
  36.         if join_type == 1 {
  37.                 //        用户id是否已经在线
  38.                 if _, ok := u.users[user_id]; ok {
  39.                         return 1, errors.New("该账号已被登陆")
  40.                 }
  41.         } else if join_type == 2 {
  42.                 //        如果原用户id 已经存在map内 进行销毁挤出
  43.                 if conn2, ok := u.users[user_id]; ok {
  44.                         err := conn2.Close()
  45.                         if err != nil {
  46.                                 fmt.Println(err)
  47.                         }
  48.                         delete(u.users, user_id)
  49.                 }
  50.                 //        重新加入
  51.                 u.users[user_id] = conn
  52.         }
  53.         return -1, nil
  54. }
  55. // 链接断开
  56. func (u *userSet) ConnDisconnect(user_id int, conn *websocket.Conn) error {
  57.         u.lock.Lock()
  58.         defer u.lock.Unlock()
  59.         if conn2, ok := u.users[user_id]; ok {
  60.                 if conn == conn2 {
  61.                         delete(u.users, user_id)
  62.                 }
  63.         } else {
  64.                 //        Log  不存在的链接申请断开
  65.         }
  66.         return nil
  67. }
  68. //        对单个链接发送信息
  69. func (u *userSet) SendMsgByUid(user_id int, msg interface{}) error {
  70.         var err error
  71.         if conn, ok := u.users[user_id]; ok {
  72.                 err = conn.WriteJSON(msg)
  73.         } else {
  74.                 err = errors.New("不存在的链接")
  75.         }
  76.         return err
  77. }
  78. //        对多个连接发送信息
  79. func (u *userSet) SendMsgByUidList(user_id_list []int, msg interface{}) (id_list []int, err_list []error) {
  80.         for _, user_id := range user_id_list {
  81.                 // 这里判断用户是否自己,是自己就跳过
  82.                 c := msg.(model.ChatMsg)
  83.                 if c.ChatMsgType == 1 {
  84.                         if (c.Data["form_user_id"].(int)) == user_id {
  85.                                 continue
  86.                         }
  87.                 }
  88.                 if conn, ok := u.users[user_id]; ok {
  89.                         err := conn.WriteJSON(msg)
  90.                         if err != nil {
  91.                                 id_list = append(id_list, user_id)
  92.                                 err_list = append(err_list, err)
  93.                         }
  94.                 } else {
  95.                         id_list = append(id_list, user_id)
  96.                         err_list = append(err_list, errors.New("不存在的链接"))
  97.                 }
  98.         }
  99.         return
  100. }
复制代码
room_set.go
[code]package manage_socket_connimport (        "sync")//群map 用来存储每个群在线的用户idtype roomSet struct {        //                 群id                群内的用户id        rooms map[int]map[int]struct{}        lock sync.Mutex        once sync.Once}var rs = new(roomSet)//        单例func GetRoomSet() *roomSet{        rs.once.Do(func() {                rs.rooms = make(map[int]map[int]struct{})                rs.lock = sync.Mutex{}        })        return rs}//        向用户发送func (r *roomSet)SendMsgToUserList (r_id int ,msg interface{}){        userS := GetUserSet()        r.lock.Lock()        defer r.lock.Unlock()        var user_id_list []int        for key, _ := range r.rooms[r_id] {                user_id_list = append(user_id_list, key)        }        userS.SendMsgByUidList(user_id_list,msg)}//        用户下线/退群 退出聊天室链接集合func (r *roomSet) UserQuitRooms(room_id_list []int ,user_id int)  {        r.lock.Lock()        defer r.lock.Unlock()        for _, room_id := range room_id_list {                if v ,ok := r.rooms[room_id];ok {                        delete(v,user_id)                        //        房间没人就销毁                        if len(r.rooms[room_id])




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4