goProject/trunk/framework/websocketServer/hookHandler.go

207 lines
5.2 KiB
Go
Raw Normal View History

2025-01-06 16:01:02 +08:00
// ************************************
// @package: websocketServer
// @description:
// @author:
// @revision history:
// @create date: 2022-02-16 18:13:45
// ************************************
package websocketServer
import (
"time"
"github.com/gorilla/websocket"
webServer "Framework/webServer"
logUtil "goutil/logUtil"
)
// 事件回调函数
type EventCallbackFuncs struct {
// websocket连接事件
OnConnFunc func(ctx *Context)
// websocket关闭事件
OnCloseFunc func(ctx *Context)
// websocket接收事件
OnMsgFunc func(ctx *Context, msgType int, msgData []byte)
}
// 用户自定义数据结构
type userDatas struct {
// WsServer 或 WssServer 指针
server interface{}
// 事件回调函数
eventCallback *EventCallbackFuncs
}
// iServerMgr
// @description: WsServer/WssServer内部管理接口以便hookHandler中统一访问
type iServerMgr interface {
// 升级为websocket
upgrade(ctx *Context) (conn *websocket.Conn, err error)
// 将连接从连接池删除
delConn(conn *websocket.Conn)
// 获取接收到Ping消息时是否自动回复Pong信息
GetAutoPong() bool
}
// 回调勾子将http/https升级为websocket
func hookHandler(webServerCtx *webServer.Context) {
defer func() {
if r := recover(); r != nil {
logUtil.LogUnknownError(r)
}
}()
userDataI := webServerCtx.GetUserData()
if userDataI == nil {
return
}
userData, ok := userDataI.(*userDatas)
if !ok {
logUtil.ErrorLog("userData type error")
return
}
// 通信结束信号
cls := make(chan struct{})
ctx := &Context{
webServerCtx: webServerCtx, // web_server环境
cls: cls, // 关闭连接信号
}
var serverMgr iServerMgr
var conn *websocket.Conn
var err error
// 转为iServerMgr
switch userData.server.(type) {
case *WsServer:
if svr, ok := userData.server.(*WsServer); ok {
serverMgr = svr
} else {
logUtil.ErrorLog("server type not WsServer")
return
}
case *WssServer:
if svr, ok := userData.server.(*WssServer); ok {
serverMgr = svr
} else {
logUtil.ErrorLog("server type not WssServer")
return
}
default:
logUtil.ErrorLog("server type not WsServer or WssServer")
return
}
// 升级为websocket
conn, err = serverMgr.upgrade(ctx)
if err != nil {
if err.Error() == "connManager(disableNewConn)" {
// 禁用新连接。正常功能,直接返回
return
}
logUtil.ErrorLog("websocket Upgrade failed: %v", err)
return
}
// 将连接从连接池删除
defer serverMgr.delConn(conn)
// 关闭连接
defer conn.Close()
// 默认情况下ReadMessage不会读取到ping/pong/close消息(内部有专门的处理函数)
// 设置心跳包处理函数
conn.SetPingHandler(func(msg string) error {
// 只要收到消息,都需要更新最近一次收到心跳包的时间
ctx.heartbeat = time.Now()
if serverMgr.GetAutoPong() {
// 自动回应一个Pong心跳
go ctx.SendMessage(websocket.PongMessage, []byte(msg))
}
// 接收消息回调
if userData.eventCallback.OnMsgFunc != nil {
userData.eventCallback.OnMsgFunc(ctx, websocket.PingMessage, []byte(msg))
}
return nil
})
// 设置关闭包处理函数
conn.SetCloseHandler(func(code int, text string) error {
// 所有向cls写入都使用select超时结构以保证这儿不会一直阻塞确保此协程能退出
select {
case <-time.After(time.Millisecond * 10):
case cls <- struct{}{}:
}
return nil
})
// 设置最近一次收到心跳包的时间
ctx.heartbeat = time.Now()
// 新连接回调
if userData.eventCallback.OnConnFunc != nil {
userData.eventCallback.OnConnFunc(ctx)
}
// 开启读协程
go func() {
defer func() {
if r := recover(); r != nil {
logUtil.LogUnknownError(r)
// 有异常出现(可能是用户回调中出现异常);执行到这儿,需要关闭连接
// 所有向cls写入都使用select超时结构以保证这儿不会一直阻塞确保此协程能退出
select {
case <-time.After(time.Millisecond * 10):
case cls <- struct{}{}:
}
}
}()
for {
// 注意ReadMessage不会读取到心跳包和关闭包数据心跳包/关闭包需要设置专门的处理函数
// 但内部对心跳包/关闭包的处理也是由ReadMessage函数触发的(也就是不调用ReadMessage函数可能也不会触发对心跳包/关闭包的处理)
// 经测试和内部代码确认:调用心跳包/关闭包处理函数时ReadMessage不会返回心跳包/关闭包处理函数调用完毕后ReadMessage才可能返回
mt, msg, err := conn.ReadMessage()
if err != nil {
// 所有向cls写入都使用select超时结构以保证这儿不会一直阻塞确保此协程能退出
select {
case <-time.After(time.Millisecond * 10):
case cls <- struct{}{}:
}
break
}
// 只要收到消息,都需要更新最近一次收到心跳包的时间
ctx.heartbeat = time.Now()
// 接收消息回调
if userData.eventCallback.OnMsgFunc != nil {
userData.eventCallback.OnMsgFunc(ctx, mt, msg)
}
}
}()
// 等待退出
<-cls
// 设置已关闭标志
ctx.isClosed = true
// 关闭回调
if userData.eventCallback.OnCloseFunc != nil {
userData.eventCallback.OnCloseFunc(ctx)
}
}