goProject/.svn/pristine/43/43442b142f5c5969f7878c2074d3b3ebe0f9c003.svn-base

451 lines
11 KiB
Plaintext
Raw Normal View History

2025-01-06 16:21:36 +08:00
// ************************************
// @package: websocketServer
// @description: websocket管理
// @author:
// @revision history:
// @create date: 2022-02-18 15:38:17
// ************************************
package websocketServer
import (
"errors"
"github.com/gorilla/websocket"
routineCtrlUtil "goutil/routineCtrlUtil"
"sync"
"time"
)
const (
// 默认广播并发数
con_DEFAULT_BROADCAST_CONCURRENT = 10
)
// connManager
// @description: websocket连接管理
type connManager struct {
// 是否禁止新连接
disableNewConn bool
// 广播并发数
broadcastConcurrent int
// websocket服务端配置结构
upgrader *websocket.Upgrader
// 广播锁-限制 消息广播/关闭所有连接 并发访问
muBroadcast sync.Mutex
// 连接池map写锁(增加/删除)
muAllConns sync.Mutex
// 连接池-所有已连接的websocket
allConns map[*websocket.Conn]*Context
//-------------------
// 心跳控制
// 接收到Ping消息时是否自动回复Pong
autuPong bool
// 心跳周期
heartbeatCycle time.Duration
// 断连周期数(超过几个心跳周期即自动关闭连接)设置为0即关闭心跳检测功能
heartbeatCloseCount int
// 是否已开启心跳检测协程
isHeartbeatDetectStart bool
}
// heartbeatDetect
// @description: 开启心跳检测协程
// parameter:
//
// @receiver connMgr:
//
// return:
func (connMgr *connManager) heartbeatDetect() {
// 限制每个websocket连接管理只开启一个心跳检测协程
if !connMgr.isHeartbeatDetectStart {
connMgr.isHeartbeatDetectStart = true
// 开启心跳检测协程
go func() {
for {
if connMgr.heartbeatCloseCount <= 0 {
// 心跳检测功能已关闭;每秒检测此标志
time.Sleep(time.Second)
continue
}
// 心跳检测功能已开启
connMgr.muAllConns.Lock() // 连接池map锁
ctxs_timeout := make([]*Context, 0, len(connMgr.allConns)) // 存放心跳超时需要关闭的websocket环境
for _, ctx := range connMgr.allConns {
if time.Since(ctx.heartbeat) > (connMgr.heartbeatCycle*time.Duration(connMgr.heartbeatCloseCount) + 1) {
// 心跳超时需要关闭的websocket环境加入列表
ctxs_timeout = append(ctxs_timeout, ctx)
}
}
connMgr.muAllConns.Unlock() // 连接池map及时解锁
// 关闭所有心跳超时的连接
func() {
// 获取广播并发数
broadcastConcurrent := connMgr.broadcastConcurrent
if broadcastConcurrent <= 0 {
broadcastConcurrent = con_DEFAULT_BROADCAST_CONCURRENT
}
// 协程并发限制
rtCtrl := routineCtrlUtil.New(broadcastConcurrent)
for _, ctx := range ctxs_timeout {
ctxTemp := ctx
rtCtrl.Run(func(arg interface{}) {
// 执行受限并发函数
ctxTemp.Close()
}, nil)
}
// 等待完成
rtCtrl.Wait()
}()
// 休眠半个心跳周期
slpTime := time.Duration(connMgr.heartbeatCycle / 2)
if slpTime < time.Second {
slpTime = time.Second
}
time.Sleep(slpTime)
}
}()
}
}
// upgrade
// @description: 升级为websocket
// parameter:
//
// @receiver connMgr:
// @ctx: websocket环境
//
// return:
//
// @*websocket.Conn: 建立的连接对象
// @error:
func (connMgr *connManager) upgrade(ctx *Context) (conn *websocket.Conn, err error) {
if connMgr.disableNewConn {
// 禁止新连接
return nil, errors.New("connManager(disableNewConn)")
}
// 建立websocket连接
conn, err = connMgr.upgrader.Upgrade(ctx.GetWebServerContext().GetResponseWriter(), ctx.GetWebServerContext().GetRequest(), nil)
if err != nil {
return
}
// 添加到连接池
ctx.conn = conn
connMgr.addConn(conn, ctx)
return
}
// addConn
// @description: 添加到连接池
// parameter:
//
// @receiver connMgr:
// @conn:
// @ctx:
//
// return:
func (connMgr *connManager) addConn(conn *websocket.Conn, ctx *Context) {
connMgr.muAllConns.Lock()
defer connMgr.muAllConns.Unlock()
connMgr.allConns[conn] = ctx
}
// delConn
// @description: 将连接从连接池删除
// parameter:
//
// @receiver connMgr:
// @conn:
//
// return:
func (connMgr *connManager) delConn(conn *websocket.Conn) {
connMgr.muAllConns.Lock()
defer connMgr.muAllConns.Unlock()
delete(connMgr.allConns, conn)
}
// renewAllConnsMap
// @description: 重新替换一个新allConns的map结构以免被标记删除的冗余信息造成存储和性能问题
// parameter:
//
// @receiver connMgr:
//
// return:
//
// @map[*websocket.Conn]*Context: 返回原内部使用的连接池(现内部已不再使用)
func (connMgr *connManager) renewAllConnsMap() map[*websocket.Conn]*Context {
connMgr.muAllConns.Lock()
defer connMgr.muAllConns.Unlock()
// map拷贝
allConnsCopy := make(map[*websocket.Conn]*Context, len(connMgr.allConns))
for conn, ctx := range connMgr.allConns {
allConnsCopy[conn] = ctx
}
// map替换因map删除时只是标记并非真正删除使用一段时间后可能会出现大量未使用信息这里顺便更新一下map
connMgr.allConns, allConnsCopy = allConnsCopy, connMgr.allConns
return allConnsCopy
}
// SetBroadcastConcurrent
// @description: 设置广播并发数
// parameter:
//
// @receiver connMgr:
// @n: 广播并发数
//
// return:
func (connMgr *connManager) SetBroadcastConcurrent(n int) {
connMgr.broadcastConcurrent = n
}
// EnableNewConn
// @description: 允许新连接
// parameter:
//
// @receiver connMgr:
//
// return:
func (connMgr *connManager) EnableNewConn() {
connMgr.disableNewConn = false
}
// DisableNewConn
// @description: 禁用新连接
// parameter:
//
// @receiver connMgr:
//
// return:
func (connMgr *connManager) DisableNewConn() {
connMgr.disableNewConn = true
}
// MulticastMessage
// @description: 多播消息(给指定多用户发送消息)
// parameter:
//
// @receiver connMgr:
// @ctxs: 指定多用户的*Context切片
// @messageType: websocket类型
// @data: 发送的数据
//
// return:
//
// @err: 若有错误,则为最后一个错误
func (connMgr *connManager) MulticastMessage(ctxs []*Context, messageType int, data []byte) (err error) {
// 广播锁,防重入
connMgr.muBroadcast.Lock()
defer connMgr.muBroadcast.Unlock()
// 获取广播并发数
broadcastConcurrent := connMgr.broadcastConcurrent
if broadcastConcurrent <= 0 {
broadcastConcurrent = con_DEFAULT_BROADCAST_CONCURRENT
}
// 协程并发限制
rtCtrl := routineCtrlUtil.New(broadcastConcurrent)
var mu sync.Mutex
for _, ctx := range ctxs {
// 执行受限并发函数
// 注意这里的ctx需要使用参数传入Run否则随时变化的ctx在闭包内使用时会出现不符合程序要求逻辑的结果
rtCtrl.Run(func(arg interface{}) {
ctxTemp, _ := arg.(*Context)
e := ctxTemp.SendMessage(messageType, data)
if e != nil {
mu.Lock()
err = e
mu.Unlock()
}
}, ctx)
}
// 等待完成
rtCtrl.Wait()
return
}
// BroadcastMessage
// @description: 消息广播
// parameter:
//
// @receiver connMgr:
// @messageType: websocket类型
// @data: 发送的数据
//
// return:
//
// @err: 若有错误,则为最后一个错误
func (connMgr *connManager) BroadcastMessage(messageType int, data []byte) (err error) {
// 广播锁,防重入
connMgr.muBroadcast.Lock()
defer connMgr.muBroadcast.Unlock()
// 重新替换一个新allConns的map结构以免被标记删除的冗余信息造成存储和性能问题
allConnsCopy := connMgr.renewAllConnsMap()
// 获取广播并发数
broadcastConcurrent := connMgr.broadcastConcurrent
if broadcastConcurrent <= 0 {
broadcastConcurrent = con_DEFAULT_BROADCAST_CONCURRENT
}
// 协程并发限制
rtCtrl := routineCtrlUtil.New(broadcastConcurrent)
var mu sync.Mutex
for _, ctx := range allConnsCopy {
// 执行受限并发函数
// 注意这里的ctx需要使用参数传入Run否则随时变化的ctx在闭包内使用时会出现不符合程序要求逻辑的结果
rtCtrl.Run(func(arg interface{}) {
ctxTemp, _ := arg.(*Context)
e := ctxTemp.SendMessage(messageType, data)
if e != nil {
mu.Lock()
err = e
mu.Unlock()
}
}, ctx)
}
// 等待完成
rtCtrl.Wait()
return
}
// CloseAll
// @description: 关闭所有连接
// parameter:
//
// @receiver connMgr:
//
// return:
func (connMgr *connManager) CloseAll() {
// 广播锁,防重入
connMgr.muBroadcast.Lock()
defer connMgr.muBroadcast.Unlock()
// 重新替换一个新allConns的map结构以免被标记删除的冗余信息造成存储和性能问题
allConnsCopy := connMgr.renewAllConnsMap()
// 获取广播并发数
broadcastConcurrent := connMgr.broadcastConcurrent
if broadcastConcurrent <= 0 {
broadcastConcurrent = con_DEFAULT_BROADCAST_CONCURRENT
}
// 协程并发限制
rtCtrl := routineCtrlUtil.New(broadcastConcurrent)
for _, ctx := range allConnsCopy {
rtCtrl.Run(func(arg interface{}) {
ctxTemp, _ := arg.(*Context)
// 执行受限并发函数
ctxTemp.Close()
}, ctx)
}
// 等待完成
rtCtrl.Wait()
}
// SetUpgrader
// @description: 设置websocket参数结构
// parameter:
//
// @receiver connMgr:
// @upgrader: websocket中的websocket.Upgrader结构体指针可以设置握手超时/读写缓存/是否允许跨域等)
//
// return:
func (connMgr *connManager) SetUpgrader(upgrader *websocket.Upgrader) {
connMgr.upgrader = upgrader
}
// GetUpgrader
// @description: 获取websocket参数结构
// parameter:
//
// @receiver connMgr:
//
// return:
//
// @*websocket.Upgrader: websocket中的websocket.Upgrader结构体指针可以设置握手超时/读写缓存/是否允许跨域等)
func (connMgr *connManager) GetUpgrader() *websocket.Upgrader {
return connMgr.upgrader
}
// SetAutoPong
// @description: 设置接收到Ping消息时是否自动回复Pong信息
// parameter:
//
// @receiver connMgr:
// @autuPong:
//
// return:
func (connMgr *connManager) SetAutoPong(autuPong bool) {
connMgr.autuPong = autuPong
}
// GetAutoPong
// @description: 获取接收到Ping消息时是否自动回复Pong信息
// parameter:
//
// @receiver connMgr:
//
// return:
//
// @bool:
func (connMgr *connManager) GetAutoPong() bool {
return connMgr.autuPong
}
// SetHeartbeatDetectInfo
// @description: 设置心跳检测信息
// parameter:
//
// @receiver connMgr:
// @heartbeatCloseCount: 断连周期数(超过几个心跳周期即自动关闭连接)设置为0即关闭心跳检测功能
// @heartbeatCycle: 心跳周期
//
// return:
func (connMgr *connManager) SetHeartbeatDetectInfo(heartbeatCloseCount int, heartbeatCycle time.Duration) {
connMgr.heartbeatCycle = heartbeatCycle
connMgr.heartbeatCloseCount = heartbeatCloseCount
}
// GetHeartbeatDetectInfo
// @description: 获取心跳检测信息
// parameter:
//
// @receiver connMgr:
//
// return:
//
// @heartbeatCloseCount: 断连周期数(超过几个心跳周期即自动关闭连接)
// @heartbeatCycle: 心跳周期
func (connMgr *connManager) GetHeartbeatDetectInfo() (heartbeatCloseCount int, heartbeatCycle time.Duration) {
return connMgr.heartbeatCloseCount, connMgr.heartbeatCycle
}