我是HullQin,公众号线下团聚游戏的作者(欢送关注公众号,发送加微信,交个敌人),转发本文前需取得作者HullQin受权。我独立开发了《联机桌游合集》,是个网页,能够很不便的跟敌人联机玩斗地主、五子棋等游戏,不免费没广告。还开发了《Dice Crush》加入Game Jam 2022。喜爱能够关注我 HullQin 噢~我有空了会分享做游戏的相干技术。

背景

在专栏《Go WebSocket》里,有一些前置文章:

第一篇文章:《为什么我选用Go重构Python版本的WebSocket服务?》,介绍了我的指标。

第二篇文章:《你的第一个Go WebSocket服务: echo server》,介绍了一下怎么写一个WebSocket server。

第三篇文章:《单房间的聊天室》,介绍了如何实现一个单房间的聊天室。

第四篇文章:《多房间的聊天室(一)思考篇》,介绍了实现一个多房间的聊天室的思路。

第五篇文章:《多房间的聊天室(二)代码实现》,介绍了实现一个多房间的聊天室的代码。

如果你没浏览下面的文章,肯定要先看一下,因为这篇文章更简单,如果你不弄懂下面几篇,这篇可能跟不上节奏噢。

上篇文章咱们提到:

当初房间数只会源源不断的增多,house这个map会越来越大,终将造成内存不足,这不是一个好事件。

所以咱们后续须要加一个优化:当最初一个客户端断开连接时,回收(删除)这个房间。

明天,咱们实现它。

思路

有一个重要的问题须要想分明:

是在哪个中央执行这个【回收】操作?是哪个goroutine?什么机会?若有多个中央,有没有竞争关系?

回顾一下之前绘制的图:

能够发现:每个客户端连贯会常驻2个goroutine:Read和Write。其中Read重要的职责就是unregister,这点我之前在《单房间的聊天室》强调过。

unregister就是把客户端连贯从hub中删除掉。这个时候,咱们就能够检查一下hub内是否还有其它客户端,若无,则删除。

留神,unregister只是个channel,真正的解决逻辑是写在goroutine中的,是哪个gotoutine负责接管unregister并执行逻辑呢?就是Hub。所以咱们须要批改Hub代码。

间接看源码

多房间聊天室案例代码的地址:github.com/HullQin/go-websocket-examples

chat-multi-rooms文件夹中,文章可配套commit记录浏览:

  • delete empty room 就是清理无人房间的逻辑。

开始开发

咱们以《多房间的聊天室(二)代码实现》的代码为根底,做改变。

关注hub goroutine的代码:

func (h *Hub) run() {   for {      select {      case client := <-h.register:         h.clients[client] = true      case client := <-h.unregister:         if _, ok := h.clients[client]; ok {            delete(h.clients, client)            close(client.send)         }      case message := <-h.broadcast:         for client := range h.clients {            select {            case client.send <- message:            default:               close(client.send)               delete(h.clients, client)            }         }      }   }}

能够看到case client := <-h.unregister:这段代码,就是解决unregister逻辑的。

这里删除了hub中的对应客户端。删除时,咱们检查一下h.clients是否为空即可,若为空,把hubhouse(房间汇合)删掉,再完结这个hub goroutine即可。

然而,有个问题,这里咱们要在house中删掉,是须要晓得key的,key是roomId,最好从hub的属性中取得,目前还不反对,所以还须要给hub减少一个roomId属性,不便做删除。

if len(h.clients) == 0 {   delete(house, h.roomId)   break}

上面,咱们减少roomId属性:

type Hub struct {   // Identity of room.   roomId string   // Registered clients.   clients map[*Client]bool   // Inbound messages from the clients.   broadcast chan []byte   // Register requests from the clients.   register chan *Client   // Unregister requests from clients.   unregister chan *Client}func newHub(roomId string) *Hub {   return &Hub{      roomId:     roomId,      broadcast:  make(chan []byte),      register:   make(chan *Client),      unregister: make(chan *Client),      clients:    make(map[*Client]bool),   }}

此外,还须要批改main.go,新建hub时,传入roomId

测试一下,功败垂成!(能够在delete逻辑减少个日志输入)当初断开连接时,无人房间会主动革除掉!并且下次进入时,也会新建房间,不影响失常应用!

真的没问题了吗?

我又绘制了一个图(以一个房间为例),更加残缺:

我用连线,表明了goroutine的启动关系:

  • User连贯WebSocket服务器时,会先启动serveWs goroutine
  • serveWs goroutine中,会执行register操作,这一点之前的图中并没画进去。
  • 随后serveWs goroutine启动了Read goroutineWrite goroutine,并完结本人。

这里真的是完满计划不会出错吗?留个悬念,咱们下篇文章,持续解说。

写在最初

我是HullQin,公众号线下团聚游戏的作者(欢送关注公众号,发送加微信,交个敌人),转发本文前需取得作者HullQin受权。我独立开发了《联机桌游合集》,是个网页,能够很不便的跟敌人联机玩斗地主、五子棋等游戏,不免费没广告。还开发了《Dice Crush》加入Game Jam 2022。喜爱能够关注我 HullQin 噢~我有空了会分享做游戏的相干技术。