游戏服务器核心通信层必须用 net 包直连 TCPConn,因 net/http 带 HTTP 头、状态码等冗余开销,无法满足实时对战帧同步需求,且不支持心跳、断线检测、粘包处理。
用
还是
写 TCP 服务?
游戏服务器核心通信层不能靠
——它带 HTTP 头、状态码、连接复用逻辑,纯增开销。实时对战、帧同步场景下,
包直连
才可控。
常见错误:用
接客户端二进制协议包,结果读到一半卡在等待换行或 header 解析,连接直接超时断开。
心跳、断线检测、粘包处理都得自己写,
不提供这些能力
后用
拿原始字节,别碰
除非你明确控制分包边界
如果真要用 HTTP(比如登录/配置接口),单独起一个
绑不同端口,和主游戏逻辑隔离
怎么安全地并发处理玩家连接?
每个
单独起 goroutine 是最直接做法,但必须配资源回收和上下文控制,否则 goroutine 泄漏比内存泄漏更快见底。
典型现象:压测时 CPU 稳定 100%,
持续上涨,
显示大量阻塞在
或
上。
立即学习
“
go语言免费学习笔记(深入)
”;
给每个连接 goroutine 加
,超时后主动
用
复用
缓冲区,避免高频分配触发 GC
禁止在连接 goroutine 里直接调用数据库或 RPC——改用消息队列或 worker pool 异步处理
解包为什么总出错?
游戏协议基本是二进制小端序(如 Unity 客户端默认),但 Go 的
默认不校验字节序,传错
或
就会读出完全错误的 ID、坐标、时间戳。
错误示例:
配合 Unity 发来的小端包,
变成 65536 倍大值,后续直接越界 panic。
协议文档没写字节序?抓包看前 4 字节,用
对比已知数值反推
结构体字段必须用
+
,别用
强转——跨平台或 GC 移动后地址失效
长度字段之后的数据,一定要用
包一层,防恶意客户端发超长包
热更新代码时怎么不踢玩家?
Go 本身不支持热加载,所谓“热更新”实际是进程平滑重启:新进程启动监听同一端口,旧进程等所有连接自然断开或超时退出。关键在
的文件描述符传递。
踩坑最多的是用
自己传 fd,结果新进程绑定失败,或旧进程关 listener 时连带关闭了新进程的连接。
用
或
+
更稳妥
监听
信号,在 handler 里用
从父进程继承 listener
旧进程不能立刻
,要等
或
真正难的不是换进程,是状态迁移——玩家背包、位置、技能 CD 这些内存数据,得提前存到 Redis 或本地 mmap 文件,新进程起来再捞。这块没现成方案,得自己权衡一致性与延迟。
net/httpnetnet/httpnetTCPConnhttp.Servehttp.Servernet.Listen("tcp", ":3000")conn.Read()bufio.Readerhttp.Servernet.Connruntime.NumGoroutine()pprofconn.Read()selectcontext.WithTimeoutconn.Close()sync.Pool[]byteencoding/binarybinary.Readbinary.LittleEndianbinary.BigEndianbinary.Read(r, binary.BigEndian, &header.Len)Lenhexdump -Cstruct{}binary.Readunsafe.Pointerio.LimitReader(conn, int64(expectedLen))net.Listenersyscall.Dup()github.com/freddierice/go-restartgithub.com/fsnotify/fsnotifyos/exec.Command("kill", "-USR2", pid)SIGUSR2l, err := net.FileListener(f)os.Exit()activeConnCount == 0time.AfterFunc(30*time.Second, os.Exit)