事实上,websocket与websocket之间是无法进行直接相互通信的,需要我们将数据接收后,发送给另一个websocket链接,可以理解为
人员 加入的聊天室a 1,2,3b 1,2c 1a在1发送信息,全部人都能收到a在2发送信息,c收不到,以此类推a可以与c单独发送信息,接收方不在线时,系统能正常运行
D:.│go.mod│go.sum│main.go│msg.json├─api│socket_conn.go├─manage_socket_conn //用户的websocket管理模块│char_room_thread.go //线程主要负责对信息的群发│room_set.go //聊天室房间管理,房间的创建,销毁存储房间内的用户id│user_set.go //用户websocket链接管理,信息的发送,存储所有在线的webscoket链接,用户上下线├─middleware│Cros.go├─model│socket_msg_form_front.go│to_front.go├─route│route.go└─servicechat_room.go //数据层,模拟用户加入了那些聊天室2.代码内容user_set.go
packagemainimport( "WebSocketDemo/route" Mg"WebSocketDemo/manage_socket_conn")funcmain(){ ro:=route.GetRoute() goMg.GetCharRoomThread().Start() _=ro.Run("0.0.0.0:8083")}api层socket_conn.go内容
//群map用来存储每个群在线的用户idtyperoomSetstruct{ // 群id 群内的用户id roomsmap[int]map[int]struct{} locksync.Mutex oncesync.Once}//用户map用来存储每个在线的用户id与对应的conntypeuserSetstruct{ // 用户链接集用户id=>链接对象 usersmap[int]*websocket.Conn locksync.Mutex oncesync.Once}可能有人会问,roomSet.rooms[房间id]map[用户id]struct{}这里的用户集为什么是map类型,而不是[]int类型答:想一下,当用户下线或退出群聊时,怎么在[]int内进行删除该用户的id,注意:此时的[]int是无序的,而加入群时,又要防止id重复,所以实现起来过于麻烦,倒不如使用map,go底层为你封装好的值判断,使用起来会更方便,这里的struct{}是没有意义的,仅作为占位。当我要把信息发送给群里的所有用户时,先从roomSet根据房间id拿到用户idmap,将key转化为[]int,调用userSet的SendMsgByUidList()方法这样就完成了信息的群发而一对一的单发就不再重复说了,跳过roomSet,直接发送其他内容已经在代码注释里讲得非常详细了
func(ChatRoom)GetUserRoomIds(user_idint)(r_ids[]int,errerror){ ifuser_id==1{ r_ids=[]int{1,2,3} }elseifuser_id==2{ r_ids=[]int{1,2} }elseifuser_id==3{ r_ids=[]int{1} } return}user_id与接口名称一样,然后将所有websocket进行连接
发送json数据
房间2只有用户3不存在房间里所以用户3接收不到信息
用户2发送信息
可以看到所有人都可以接收信息一对一测试用户1发送json给用户3
可以看到只有用户2无法收到一对一的信息
关于websocket系列教程就已经结束了本章重点在于如何进行设计一个websocket管理模块,对在线的用户进行管理不足点:由于怕篇幅过长,没有将聊天数据存储起来,实现原理便是在发送信息前把数据存入库,搭配gorm的事务,当有错误时便回滚,用户上线时,前端获取本地存储的聊天数据id,拉取最后的数据列表,便可做到用户上线读取未接收的数据,这里可以在api层直接实现程序的所有问题,大部分都可以通过创造性的思想进行解决,希望本篇内容能对你有所帮助欢迎大家点赞转发