mirror of
https://gitee.com/samwaf/SamWaf.git
synced 2026-11-02 19:21:44 +08:00
115 lines
3.5 KiB
Go
115 lines
3.5 KiB
Go
package api
|
|
|
|
import (
|
|
"SamWaf/common/gwebsocket"
|
|
"SamWaf/common/zlog"
|
|
"SamWaf/global"
|
|
"SamWaf/model"
|
|
"encoding/json"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
type WafWebSocketApi struct {
|
|
}
|
|
|
|
func (w *WafWebSocketApi) WebSocketMessageApi(c *gin.Context) {
|
|
var upGrader = websocket.Upgrader{
|
|
Subprotocols: []string{c.Request.Header.Get("Sec-WebSocket-Protocol")},
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
return true
|
|
},
|
|
HandshakeTimeout: 45 * time.Second, // 握手超时时间
|
|
ReadBufferSize: 1024, // 读缓冲区大小
|
|
WriteBufferSize: 1024, // 写缓冲区大小
|
|
}
|
|
//获取用户账号:
|
|
tokenStr := c.GetHeader("Sec-WebSocket-Protocol")
|
|
tokenInfo := wafTokenInfoService.GetInfoByAccessToken(tokenStr)
|
|
|
|
//本次连接声明的传输会话密钥(v2 客户端才带;旧客户端为空,推送回落 legacy)。
|
|
//WebSocket 建连没法带自定义头,只能走查询参数。
|
|
keyID := c.Query("keyid")
|
|
|
|
//升级get请求为webSocket协议
|
|
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
|
if err != nil {
|
|
zlog.Error("websocketinit", err)
|
|
return
|
|
}
|
|
|
|
if tokenInfo.LoginAccount == "" {
|
|
//写入ws数据(此时连接还没登记到管理器,只有当前协程在写)
|
|
msgBytes, _ := json.Marshal(model.MsgPacket{
|
|
MsgCode: "-999",
|
|
MsgCmdType: "Info",
|
|
})
|
|
ws.SetWriteDeadline(time.Now().Add(gwebsocket.WriteWait))
|
|
err = ws.WriteMessage(websocket.TextMessage, msgBytes)
|
|
zlog.Debug("无鉴权信息,请检查")
|
|
ws.Close() // 未鉴权连接必须显式关闭,否则连接一直挂着不释放
|
|
return
|
|
}
|
|
|
|
// 读超时靠心跳续期:前端每 30s 发一次 ping,服务端也会主动发 Ping 帧,
|
|
// 只要有一路活着就会刷新 deadline;都没了说明连接已死,读取直接报错退出。
|
|
ws.SetReadDeadline(time.Now().Add(gwebsocket.PongWait))
|
|
ws.SetPongHandler(func(string) error {
|
|
zlog.Debug("收到pong消息,连接正常")
|
|
return ws.SetReadDeadline(time.Now().Add(gwebsocket.PongWait))
|
|
})
|
|
|
|
// 生成用户标识和会话ID
|
|
userKey := tokenInfo.BaseOrm.Tenant_ID + tokenInfo.BaseOrm.USER_CODE + tokenInfo.LoginAccount
|
|
sessionID := global.GWebSocket.AddWebSocketWithKey(userKey, keyID, ws)
|
|
|
|
zlog.Debug("WebSocket连接建立,用户: " + userKey + ", 会话ID: " + sessionID)
|
|
|
|
// 服务端主动心跳:探活半开连接,同时给读超时续期
|
|
stopPing := make(chan struct{})
|
|
go func() {
|
|
ticker := time.NewTicker(gwebsocket.PingPeriod)
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-stopPing:
|
|
return
|
|
case <-ticker.C:
|
|
if err := global.GWebSocket.PingSession(sessionID); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
defer func() {
|
|
close(stopPing)
|
|
// 只关闭当前会话的连接
|
|
global.GWebSocket.CloseSession(sessionID)
|
|
zlog.Debug("WebSocket连接已关闭,会话ID: " + sessionID)
|
|
}()
|
|
|
|
for {
|
|
//读取ws中的数据(每条连接只有这一个读协程)
|
|
mt, message, err := ws.ReadMessage()
|
|
if err != nil {
|
|
break
|
|
}
|
|
ws.SetReadDeadline(time.Now().Add(gwebsocket.PongWait))
|
|
|
|
zlog.Debug("websocket msg=" + string(message) + ", 会话ID: " + sessionID)
|
|
if string(message) == "ping" {
|
|
message = []byte("pong")
|
|
}
|
|
|
|
//写入ws数据:统一走会话管理器,内部按连接加锁,避免与队列/定时任务的广播并发写
|
|
if err = global.GWebSocket.SendToSession(sessionID, mt, message); err != nil {
|
|
zlog.Debug("WebSocket回写失败,退出消息循环,会话ID: " + sessionID)
|
|
break
|
|
}
|
|
}
|
|
}
|