mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 23:40:24 +08:00
163 lines
3.9 KiB
Go
163 lines
3.9 KiB
Go
package ws
|
|
|
|
import (
|
|
"mayfly-go/pkg/logx"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// 心跳间隔
|
|
const heartbeatInterval = 25 * time.Second
|
|
|
|
// 连接管理
|
|
type ClientManager struct {
|
|
ClientMap map[UserId]*Client // 全部的连接, key->userid, value->&client
|
|
RwLock sync.RWMutex // 读写锁
|
|
|
|
ConnectChan chan *Client // 连接处理
|
|
DisConnectChan chan *Client // 断开连接处理
|
|
MsgChan chan *Msg // 消息信息channel通道
|
|
}
|
|
|
|
func NewClientManager() (clientManager *ClientManager) {
|
|
return &ClientManager{
|
|
ClientMap: make(map[UserId]*Client),
|
|
ConnectChan: make(chan *Client, 10),
|
|
DisConnectChan: make(chan *Client, 10),
|
|
MsgChan: make(chan *Msg, 100),
|
|
}
|
|
}
|
|
|
|
// 管道处理程序
|
|
func (manager *ClientManager) Start() {
|
|
manager.HeartbeatTimer()
|
|
manager.WriteMessage()
|
|
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关闭客户端连接
|
|
func (manager *ClientManager) CloseByUid(uid UserId) {
|
|
manager.CloseClient(manager.GetByUid(UserId(uid)))
|
|
}
|
|
|
|
// 获取所有的客户端
|
|
func (manager *ClientManager) AllClient() map[UserId]*Client {
|
|
manager.RwLock.RLock()
|
|
defer manager.RwLock.RUnlock()
|
|
|
|
return manager.ClientMap
|
|
}
|
|
|
|
// 通过userId获取
|
|
func (manager *ClientManager) GetByUid(userId UserId) *Client {
|
|
manager.RwLock.RLock()
|
|
defer manager.RwLock.RUnlock()
|
|
return manager.ClientMap[userId]
|
|
}
|
|
|
|
// 客户端数量
|
|
func (manager *ClientManager) Count() int {
|
|
manager.RwLock.RLock()
|
|
defer manager.RwLock.RUnlock()
|
|
return len(manager.ClientMap)
|
|
}
|
|
|
|
// 发送json数据给指定用户
|
|
func (manager *ClientManager) SendJsonMsg(userId UserId, data any) {
|
|
logx.Debugf("发送消息: toUid=%v, data=%v", userId, data)
|
|
manager.MsgChan <- &Msg{ToUserId: userId, Data: data, Type: JsonMsg}
|
|
}
|
|
|
|
// 监听并发送给客户端信息
|
|
func (manager *ClientManager) WriteMessage() {
|
|
go func() {
|
|
for {
|
|
msg := <-manager.MsgChan
|
|
if cli := manager.GetByUid(msg.ToUserId); cli != nil {
|
|
if err := cli.WriteMsg(msg); err != nil {
|
|
manager.CloseClient(cli)
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// 启动定时器进行心跳检测
|
|
func (manager *ClientManager) HeartbeatTimer() {
|
|
go func() {
|
|
ticker := time.NewTicker(heartbeatInterval)
|
|
defer ticker.Stop()
|
|
for {
|
|
<-ticker.C
|
|
//发送心跳
|
|
for userId, cli := range manager.AllClient() {
|
|
if cli == nil || cli.WsConn == nil {
|
|
continue
|
|
}
|
|
if err := cli.Ping(); err != nil {
|
|
manager.CloseClient(cli)
|
|
logx.Debugf("WS发送心跳失败: %v 总连接数:%d", userId, Manager.Count())
|
|
} else {
|
|
logx.Debugf("WS发送心跳成功: uid=%v", userId)
|
|
}
|
|
}
|
|
}
|
|
|
|
}()
|
|
}
|
|
|
|
// 处理建立连接
|
|
func (manager *ClientManager) doConnect(client *Client) {
|
|
cli := manager.GetByUid(client.UserId)
|
|
if cli != nil {
|
|
manager.doDisconnect(cli)
|
|
}
|
|
manager.addClient2Map(client)
|
|
logx.Debugf("WS客户端已连接: uid=%d, count=%d", client.UserId, manager.Count())
|
|
}
|
|
|
|
// 处理断开连接
|
|
func (manager *ClientManager) doDisconnect(client *Client) {
|
|
//关闭连接
|
|
if client.WsConn != nil {
|
|
_ = client.WsConn.Close()
|
|
client.WsConn = nil
|
|
}
|
|
manager.delClient4Map(client)
|
|
logx.Debugf("WS客户端已断开: uid=%d, count=%d", client.UserId, Manager.Count())
|
|
}
|
|
|
|
func (manager *ClientManager) addClient2Map(client *Client) {
|
|
manager.RwLock.Lock()
|
|
defer manager.RwLock.Unlock()
|
|
manager.ClientMap[client.UserId] = client
|
|
}
|
|
|
|
func (manager *ClientManager) delClient4Map(client *Client) {
|
|
manager.RwLock.Lock()
|
|
defer manager.RwLock.Unlock()
|
|
delete(manager.ClientMap, client.UserId)
|
|
}
|