2023-09-12 20:54:07 +08:00
|
|
|
|
package ws
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"mayfly-go/pkg/logx"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// 心跳间隔
|
2023-10-10 09:24:49 +08:00
|
|
|
|
const heartbeatInterval = 25 * time.Second
|
2023-09-12 20:54:07 +08:00
|
|
|
|
|
2023-10-19 19:00:23 +08:00
|
|
|
|
// 单个用户的全部的连接, key->clientId, value->Client
|
|
|
|
|
|
type UserClients map[string]*Client
|
|
|
|
|
|
|
|
|
|
|
|
func (ucs UserClients) GetByCid(clientId string) *Client {
|
|
|
|
|
|
return ucs[clientId]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (ucs UserClients) AddClient(client *Client) {
|
|
|
|
|
|
ucs[client.ClientId] = client
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (ucs UserClients) DeleteByCid(clientId string) {
|
|
|
|
|
|
delete(ucs, clientId)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (ucs UserClients) Count() int {
|
|
|
|
|
|
return len(ucs)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-12 20:54:07 +08:00
|
|
|
|
// 连接管理
|
|
|
|
|
|
type ClientManager struct {
|
2025-09-17 21:23:12 +08:00
|
|
|
|
UserClientsMap sync.Map // 全部的用户连接, key->userid, value->UserClients
|
2023-09-12 20:54:07 +08:00
|
|
|
|
|
|
|
|
|
|
ConnectChan chan *Client // 连接处理
|
|
|
|
|
|
DisConnectChan chan *Client // 断开连接处理
|
2025-09-17 21:23:12 +08:00
|
|
|
|
MsgChan chan *Msg // 消息信息channel通道
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func NewClientManager() (clientManager *ClientManager) {
|
|
|
|
|
|
return &ClientManager{
|
2025-09-17 21:23:12 +08:00
|
|
|
|
UserClientsMap: sync.Map{},
|
2023-09-12 20:54:07 +08:00
|
|
|
|
ConnectChan: make(chan *Client, 10),
|
|
|
|
|
|
DisConnectChan: make(chan *Client, 10),
|
|
|
|
|
|
MsgChan: make(chan *Msg, 100),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 管道处理程序
|
|
|
|
|
|
func (manager *ClientManager) Start() {
|
|
|
|
|
|
manager.HeartbeatTimer()
|
2023-09-19 23:00:32 +08:00
|
|
|
|
manager.WriteMessage()
|
2023-09-12 20:54:07 +08:00
|
|
|
|
for {
|
|
|
|
|
|
select {
|
|
|
|
|
|
case client := <-manager.ConnectChan:
|
|
|
|
|
|
// 建立连接
|
|
|
|
|
|
manager.doConnect(client)
|
|
|
|
|
|
case conn := <-manager.DisConnectChan:
|
|
|
|
|
|
// 断开连接
|
|
|
|
|
|
manager.doDisconnect(conn)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加客户端
|
|
|
|
|
|
func (manager *ClientManager) AddClient(client *Client) {
|
|
|
|
|
|
manager.ConnectChan <- client
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭客户端
|
|
|
|
|
|
func (manager *ClientManager) CloseClient(client *Client) {
|
|
|
|
|
|
if client == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
manager.DisConnectChan <- client
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据用户id关闭客户端连接
|
2023-10-19 19:00:23 +08:00
|
|
|
|
func (manager *ClientManager) CloseByUid(userId UserId) {
|
2025-09-17 21:23:12 +08:00
|
|
|
|
userClients := manager.GetByUid(userId)
|
|
|
|
|
|
for _, client := range userClients {
|
2023-10-19 19:00:23 +08:00
|
|
|
|
manager.CloseClient(client)
|
|
|
|
|
|
}
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有的客户端
|
2023-10-19 19:00:23 +08:00
|
|
|
|
func (manager *ClientManager) AllUserClient() map[UserId]UserClients {
|
2025-09-17 21:23:12 +08:00
|
|
|
|
result := make(map[UserId]UserClients)
|
|
|
|
|
|
manager.UserClientsMap.Range(func(key, value any) bool {
|
|
|
|
|
|
userId := key.(UserId)
|
|
|
|
|
|
userClients := value.(UserClients)
|
|
|
|
|
|
result[userId] = userClients
|
|
|
|
|
|
return true
|
|
|
|
|
|
})
|
|
|
|
|
|
return result
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-19 19:00:23 +08:00
|
|
|
|
// 通过userId获取用户所有客户端信息
|
|
|
|
|
|
func (manager *ClientManager) GetByUid(userId UserId) UserClients {
|
2025-09-17 21:23:12 +08:00
|
|
|
|
if value, ok := manager.UserClientsMap.Load(userId); ok {
|
|
|
|
|
|
return value.(UserClients)
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
2023-10-18 15:24:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-19 19:00:23 +08:00
|
|
|
|
// 通过userId和clientId获取客户端信息
|
|
|
|
|
|
func (manager *ClientManager) GetByUidAndCid(uid UserId, clientId string) *Client {
|
|
|
|
|
|
if clients := manager.GetByUid(uid); clients != nil {
|
|
|
|
|
|
return clients.GetByCid(clientId)
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 客户端数量
|
|
|
|
|
|
func (manager *ClientManager) Count() int {
|
2025-09-17 21:23:12 +08:00
|
|
|
|
count := 0
|
|
|
|
|
|
manager.UserClientsMap.Range(func(key, value any) bool {
|
|
|
|
|
|
count++
|
|
|
|
|
|
return true
|
|
|
|
|
|
})
|
|
|
|
|
|
return count
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发送json数据给指定用户
|
2023-10-19 19:00:23 +08:00
|
|
|
|
func (manager *ClientManager) SendJsonMsg(userId UserId, clientId string, data any) {
|
|
|
|
|
|
manager.MsgChan <- &Msg{ToUserId: userId, ToClientId: clientId, Data: data, Type: JsonMsg}
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 监听并发送给客户端信息
|
|
|
|
|
|
func (manager *ClientManager) WriteMessage() {
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
for {
|
|
|
|
|
|
msg := <-manager.MsgChan
|
2023-10-19 19:00:23 +08:00
|
|
|
|
uid := msg.ToUserId
|
|
|
|
|
|
cid := msg.ToClientId
|
|
|
|
|
|
// 客户端id不为空,则向指定客户端发送消息即可
|
|
|
|
|
|
if cid != "" {
|
|
|
|
|
|
cli := manager.GetByUidAndCid(uid, cid)
|
|
|
|
|
|
if cli != nil {
|
2023-10-26 17:15:49 +08:00
|
|
|
|
if err := cli.WriteMsg(msg); err != nil {
|
2025-05-20 21:04:47 +08:00
|
|
|
|
logx.Warnf("ws send message failed - [uid=%d, cid=%s]: %s", uid, cid, err.Error())
|
2023-10-26 17:15:49 +08:00
|
|
|
|
}
|
2023-10-19 19:00:23 +08:00
|
|
|
|
} else {
|
2025-05-20 21:04:47 +08:00
|
|
|
|
logx.Warnf("[uid=%v, cid=%s] ws conn not exist", uid, cid)
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
2023-10-19 19:00:23 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// cid为空,则向该用户所有客户端发送该消息
|
2025-09-17 21:23:12 +08:00
|
|
|
|
userClients := manager.GetByUid(uid)
|
|
|
|
|
|
for _, cli := range userClients {
|
2023-10-26 17:15:49 +08:00
|
|
|
|
if err := cli.WriteMsg(msg); err != nil {
|
2025-05-20 21:04:47 +08:00
|
|
|
|
logx.Warnf("ws send message failed - [uid=%d, cid=%s]: %s", uid, cli.ClientId, err.Error())
|
2023-10-26 17:15:49 +08:00
|
|
|
|
}
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 启动定时器进行心跳检测
|
|
|
|
|
|
func (manager *ClientManager) HeartbeatTimer() {
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
ticker := time.NewTicker(heartbeatInterval)
|
|
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
for {
|
|
|
|
|
|
<-ticker.C
|
|
|
|
|
|
//发送心跳
|
2025-09-17 21:23:12 +08:00
|
|
|
|
manager.UserClientsMap.Range(func(key, value any) bool {
|
|
|
|
|
|
userId := key.(UserId)
|
|
|
|
|
|
clis := value.(UserClients)
|
2023-10-19 19:00:23 +08:00
|
|
|
|
for _, cli := range clis {
|
|
|
|
|
|
if cli == nil || cli.WsConn == nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
if err := cli.Ping(); err != nil {
|
|
|
|
|
|
manager.CloseClient(cli)
|
2025-09-17 21:23:12 +08:00
|
|
|
|
logx.Debugf("WS - failed to send heartbeat: uid=%v, cid=%s, usercount=%d", userId, cli.ClientId, manager.Count())
|
2023-10-19 19:00:23 +08:00
|
|
|
|
} else {
|
2024-11-23 17:23:18 +08:00
|
|
|
|
logx.Debugf("WS - send heartbeat successfully: uid=%v, cid=%s", userId, cli.ClientId)
|
2023-10-19 19:00:23 +08:00
|
|
|
|
}
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
2025-09-17 21:23:12 +08:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理建立连接
|
|
|
|
|
|
func (manager *ClientManager) doConnect(client *Client) {
|
2023-10-19 19:00:23 +08:00
|
|
|
|
cli := manager.GetByUidAndCid(client.UserId, client.ClientId)
|
2023-09-12 20:54:07 +08:00
|
|
|
|
if cli != nil {
|
|
|
|
|
|
manager.doDisconnect(cli)
|
|
|
|
|
|
}
|
2023-10-19 19:00:23 +08:00
|
|
|
|
manager.addUserClient2Map(client)
|
2025-05-20 21:04:47 +08:00
|
|
|
|
logx.Debugf("WS client connected: uid=%d, cid=%s, usercount=%d", client.UserId, client.ClientId, manager.Count())
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理断开连接
|
|
|
|
|
|
func (manager *ClientManager) doDisconnect(client *Client) {
|
|
|
|
|
|
//关闭连接
|
2023-09-16 17:07:48 +08:00
|
|
|
|
if client.WsConn != nil {
|
|
|
|
|
|
_ = client.WsConn.Close()
|
|
|
|
|
|
client.WsConn = nil
|
|
|
|
|
|
}
|
2023-10-19 19:00:23 +08:00
|
|
|
|
manager.delUserClient4Map(client)
|
2025-09-17 21:23:12 +08:00
|
|
|
|
logx.Debugf("WS client disconnected: uid=%d, cid=%s, usercount=%d", client.UserId, client.ClientId, manager.Count())
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-19 19:00:23 +08:00
|
|
|
|
func (manager *ClientManager) addUserClient2Map(client *Client) {
|
2025-09-17 21:23:12 +08:00
|
|
|
|
// 先尝试加载现有的UserClients
|
|
|
|
|
|
if value, ok := manager.UserClientsMap.Load(client.UserId); ok {
|
|
|
|
|
|
userClients := value.(UserClients)
|
|
|
|
|
|
userClients.AddClient(client)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 创建新的UserClients
|
|
|
|
|
|
userClients := make(UserClients)
|
|
|
|
|
|
userClients.AddClient(client)
|
|
|
|
|
|
manager.UserClientsMap.Store(client.UserId, userClients)
|
2023-10-19 19:00:23 +08:00
|
|
|
|
}
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-19 19:00:23 +08:00
|
|
|
|
func (manager *ClientManager) delUserClient4Map(client *Client) {
|
2025-09-17 21:23:12 +08:00
|
|
|
|
if value, ok := manager.UserClientsMap.Load(client.UserId); ok {
|
|
|
|
|
|
userClients := value.(UserClients)
|
2023-10-19 19:00:23 +08:00
|
|
|
|
userClients.DeleteByCid(client.ClientId)
|
|
|
|
|
|
// 如果用户所有客户端都关闭,则移除manager中的UserClientsMap值
|
|
|
|
|
|
if userClients.Count() == 0 {
|
2025-09-17 21:23:12 +08:00
|
|
|
|
manager.UserClientsMap.Delete(client.UserId)
|
2023-10-19 19:00:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-09-12 20:54:07 +08:00
|
|
|
|
}
|