Files
bl/common/socket/ServerEvent.go
昔念 30ed47a30c ```
refactor(socket): 优化消息处理逻辑,避免顺序执行问题

将消息处理的循环从协程外部移入协程内部,确保每个消息在独立的 goroutine 中处理,
避免因并发导致的消息顺序错乱问题。同时移除了多余的空行,使代码更简洁。

fix(controller): 为低 ID 用户设置 VIP 标志

在 COMMEND_ONLINE 接口逻辑中,新增对 UserID 小于 10000 的用户设置 IsVip = 1,
用于标识测试或特殊用户身份。

ref
2025-11-03 03:59:59 +08:00

209 lines
4.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package socket
import (
"context"
"log"
"os"
"sync/atomic"
"time"
"blazing/common/socket/codec"
"blazing/cool"
"blazing/logic/service/player"
"github.com/panjf2000/gnet/v2"
)
func (s *Server) Boot() error {
// go s.bootws()
err := gnet.Run(s, s.network+"://"+s.addr,
gnet.WithMulticore(true),
gnet.WithTicker(true),
// gnet.WithReusePort(true),
// gnet.WithReuseAddr(true),
gnet.WithSocketRecvBuffer(s.bufferSize))
if err != nil {
panic(err)
}
return nil
}
func (s *Server) Stop() error {
_ = s.eng.Stop(context.Background())
s.workerPool.Release()
return nil
}
func (s *Server) OnClose(c gnet.Conn, _ error) (action gnet.Action) {
defer func() {
if err := recover(); err != nil { // 恢复 panicerr 为 panic 错误值
// 1. 打印错误信息
cool.Loger.Error(context.TODO(), "panic 错误:", err)
}
}()
atomic.AddInt64(&s.connected, -1)
//logging.Infof("conn[%v] disconnected", c.RemoteAddr().String())
v, _ := c.Context().(*player.ClientData)
if v.Player != nil {
v.SaveL.Do(func() { //使用保存锁,确保在踢人和掉线的时候只保存一次
//cool.Loger.Info(context.TODO(), "准备保存", v.Player.Info.UserID)
v.Player.Save() //保存玩家数据
})
}
//}
//关闭连接
return
}
func (s *Server) OnTick() (delay time.Duration, action gnet.Action) {
cool.Loger.Infof(context.Background(), "[connected-count=%v]", atomic.LoadInt64(&s.connected))
if s.quit && atomic.LoadInt64(&s.connected) == 0 {
//执行正常退出逻辑
os.Exit(0)
}
return 30 * time.Second, gnet.None
}
func (s *Server) OnBoot(eng gnet.Engine) gnet.Action {
s.eng = eng
// cool.Loger.Infof(context.Background(), " server is listening on %s\n", s.addr)
return gnet.None
}
func (s *Server) OnOpen(conn gnet.Conn) (out []byte, action gnet.Action) {
if s.network != "tcp" {
return nil, gnet.Close
}
if conn.Context() == nil {
conn.SetContext(player.NewClientData(conn)) //注入data
}
atomic.AddInt64(&s.connected, 1)
return nil, gnet.None
}
func (s *Server) OnTraffic(c gnet.Conn) (action gnet.Action) {
defer func() {
if err := recover(); err != nil { // 恢复 panicerr 为 panic 错误值
// 1. 打印错误信息
cool.Loger.Error(context.TODO(), "panic 错误:", err)
}
}()
ws := c.Context().(*player.ClientData).Wsmsg
if ws.Tcp { //升级失败时候防止缓冲区溢出
return s.handleTcp(c)
}
tt, len1 := ws.ReadBufferBytes(c)
if tt == gnet.Close {
return gnet.Close
}
ok, action := ws.Upgrade(c)
if action != gnet.None { //连接断开
return action
}
if !ok { //升级失败,说明是tcp连接
ws.Tcp = true
return s.handleTcp(c)
}
// fmt.Println(ws.Buf.Bytes())
c.Discard(len1)
messages, err := ws.Decode(c)
if err != nil {
return gnet.Close
}
if messages == nil {
return
}
t := c.Context().(*player.ClientData)
//client := conn.RemoteAddr().String()
s.workerPool.Submit(func() { //TODO 这里可能存在顺序执行问题,待修复
for _, msg := range messages {
t.OnEvent(msg.Payload)
}
})
return gnet.None
}
func (s *Server) handleTcp(conn gnet.Conn) (action gnet.Action) {
conn.Context().(*player.ClientData).IsCrossDomain.Do(func() { //跨域检测
handle(conn)
})
data, err := s.codec.Decode(conn)
if err != nil {
if err == codec.ErrIncompletePacket {
if err := conn.Wake(nil); err != nil { // wake up the connection manually to avoid missing the leftover data
cool.Loger.Errorf(context.Background(), "failed to wake up the connection, %v", err)
return gnet.Close
}
}
return gnet.Close
}
s.workerPool.Submit(func() { //TODO 这里可能存在顺序执行问题,待修复
conn.Context().(*player.ClientData).OnEvent(data)
})
if conn.InboundBuffered() > 0 {
if err := conn.Wake(nil); err != nil { // wake up the connection manually to avoid missing the leftover data
cool.Loger.Errorf(context.Background(), "failed to wake up the connection, %v", err)
return gnet.Close
}
}
return action
}
// CROSS_DOMAIN 定义跨域策略文件内容
const CROSS_DOMAIN = "<?xml version=\"1.0\"?><!DOCTYPE cross-domain-policy><cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\x00"
// TEXT 定义跨域请求的文本格式
const TEXT = "<policy-file-request/>\x00"
func handle(c gnet.Conn) {
// 读取数据并检查是否为跨域请求
data, err := c.Peek(len(TEXT))
if err != nil {
log.Printf("Error reading cross-domain request: %v", err)
return
}
if string(data) == TEXT { //判断是否是跨域请求
log.Printf("Received cross-domain request from %s", c.RemoteAddr())
// 处理跨域请求
c.Write([]byte(CROSS_DOMAIN))
c.Discard(len(TEXT))
return
}
//return
}