处理聊天室相应功能的是RoomClient
,它继承自Connection
。
一个终端一次最多只能进入一个聊天室,RoomClient会保存字段:room_id。
type RoomClient struct {
*Connection
room_id int64
}
func (client *Client) HandleMessage(msg *Message) {
switch msg.cmd {
case MSG_AUTH_TOKEN:
client.HandleAuthToken(msg.body.(*AuthenticationToken), msg.version)
case MSG_ACK:
client.HandleACK(msg.body.(*MessageACK))
case MSG_PING:
client.HandlePing()
}
client.PeerClient.HandleMessage(msg)
client.GroupClient.HandleMessage(msg)
client.RoomClient.HandleMessage(msg)
client.CustomerClient.HandleMessage(msg)
}
从代码可以看出,IM服务器接收到终端发来的消息后,会分发到四种类型的Client:
具体的不同类型Client是否需要处理某种类型的消息,由其自己决定。
RoomClient只处理三种类型的消息:
func (client *RoomClient) HandleMessage(msg *Message) {
switch msg.cmd {
case MSG_ENTER_ROOM:
client.HandleEnterRoom(msg.body.(*Room))
case MSG_LEAVE_ROOM:
client.HandleLeaveRoom(msg.body.(*Room))
case MSG_ROOM_IM:
client.HandleRoomIM(msg.body.(*RoomMessage), msg.seq)
}
}
func (client *RoomClient) HandleEnterRoom(room *Room){
if client.uid == 0 {
//如果终端没有进行登录认证,那么do nothing
log.Warning("client has't been authenticated")
return
}
room_id := room.RoomID()
if room_id == 0 || client.room_id == room_id {
//如果room_id不合法或者终端已经在这个聊天室了,那么do nothing
return
}
route := app_route.FindOrAddRoute(client.appid)
if client.room_id > 0 {
//如果终端已经在另外一个聊天室了,那么必须先退出那个聊天室
channel := GetRoomChannel(client.room_id)
//告知IMR路由服务器退出聊天室,维护IMR的聊天室路由信息
channel.UnsubscribeRoom(client.appid, client.room_id)
//修改当前IM服务器上的聊天室路由信息
route.RemoveRoomClient(client.room_id, client.Client())
}
//进入新的聊天室
client.room_id = room_id
//修改当前IM服务器上的聊天室路由信息
route.AddRoomClient(client.room_id, client.Client())
channel := GetRoomChannel(client.room_id)
//修改IMR服务器上的聊天室路由信息
channel.SubscribeRoom(client.appid, client.room_id)
}
func (client *RoomClient) HandleLeaveRoom(room *Room) {
if client.uid == 0 {
//如果终端没有进行登录认证,那么do nothing
log.Warning("client has't been authenticated")
return
}
room_id := room.RoomID()
if room_id == 0 {
//如果room_id不合法,那么do nothing
return
}
if client.room_id != room_id {
//如果当前所在聊天室不是准备要退出的聊天室,do nothing
return
}
route := app_route.FindOrAddRoute(client.appid)
//修改当前IM服务器上的聊天室路由信息
route.RemoveRoomClient(client.room_id, client.Client())
channel := GetRoomChannel(client.room_id)
//修改IMR服务器上的聊天室路由信息
channel.UnsubscribeRoom(client.appid, client.room_id)
//重置room_id为0,表明已退出聊天室
client.room_id = 0
}
func (client *RoomClient) HandleRoomIM(room_im *RoomMessage, seq int) {
if client.uid == 0 {
//校验是否登录验证
return
}
room_id := room_im.receiver
if room_id != client.room_id {
//校验是否非法room_id
client.room_id)
return
}
fb := atomic.LoadInt32(&client.forbidden)
if (fb == 1) {
//校验用户是否被禁言
return
}
//将聊天室消息直接转发到连接当前IM服务器的所有进入聊天室的终端
m := &Message{cmd:MSG_ROOM_IM, body:room_im}
route := app_route.FindOrAddRoute(client.appid)
clients := route.FindRoomClientSet(room_id)
for c, _ := range(clients) {
if c == client.Client() {
continue
}
c.EnqueueNonBlockMessage(m)
}
//将聊天室消息通过IMR路由服务器分发到其他进入聊天室的终端
amsg := &AppMessage{appid:client.appid, receiver:room_id, msg:m}
channel := GetRoomChannel(client.room_id)
channel.PublishRoom(amsg)
//返回给发送终端一个ACK回复
client.wt <- &Message{cmd: MSG_ACK, body: &MessageACK{int32(seq)}}
}
当终端连接断开的时候,同时会调用RoomClient.Logout()
,以退出聊天室。
Logout方法的作用就是退出聊天室,与HandleLeaveRoom比起来只是少做了一些不必要的判断。
func (client *RoomClient) Logout() {
if client.room_id > 0 {
channel := GetRoomChannel(client.room_id)
channel.UnsubscribeRoom(client.appid, client.room_id)
route := app_route.FindOrAddRoute(client.appid)
route.RemoveRoomClient(client.room_id, client.Client())
}
}