这篇是我的自建 C# 游戏服务端(CSharpGameServer:DotNetty + SqlSugar + MySQL + Redis)的知识地图。它不是教程,是把"一个能跑联机对战的服务端要懂哪些面"铺开。
一、网络层:DotNetty
- DotNetty 是 Netty 的 C# 移植:Channel/Pipeline/Handler 模型,TCP 与 WebSocket 可以共用一个端口分发(按首包特征分流)。
- 微信小游戏环境没有裸 TCP/UDP,WebSocket 是微信端的常用选择;线上必须 wss,TLS 建议在 Nginx 层终止,服务端进程只监听本地明文端口。原生端则可以直接上裸 TCP。
- 会话抽象(IdSession)要承载:连接引用、accountId/playerId 绑定、逻辑服路由 id、限流令牌桶——它是全链路的上下文载体。
二、协议路由
- 包头自定义 + protobuf 消息体。字节布局是实现细节,不进公开文档。
- 路由用特性标注:
[Controller]+[RequestMapping]反射注册,参数按类型自动注入(session/消息体/playerId)。 - 教训:错误分支必须回包——只成功才回包的处理器会让客户端永远干等;以及返回 int 状态码的处理器别当消息推(装箱 int 不是 Message,推出去就是 NRE)。
三、线程模型
- 网络线程不跑业务:收包后按 distributeKey(一般是 playerId)投递到 TaskWorkerPool 串行执行——同玩家消息天然有序,免锁。
- 逻辑服是独立线程的 20FPS 帧循环(房间服/世界服各一实例),事件队列驱动。
- 队列要有背压(上限+丢弃+节流告警),不然一波风暴直接把内存打爆。
四、数据层
- SqlSugar(ORM)+ MySQL:实体类即表结构。表名默认映射是类名小写——多词表(如 PlayerKv → playerkv vs 迁移脚本建的 player_kv)必须显式
[SugarTable]+ 主键特性,否则回源就炸。 - 缓存层(回源 + 写穿):读穿透缓存 LRU,写走统一出口(留痕+重试+死信)。缓存层裸吞异常是大忌——会把真实根因藏成下游的 NullReferenceException,必须留日志。
- Redis 只放排行这类真·共享热点,别当万能 KV 用。
五、权威与安全
- 服务端权威:客户端的一切"我赢了/我用道具成功"都只是上报,权威判定在服务端。
- 帧同步里结算走双端模拟上报+一致性校验:不一致记日志(反作弊线索),按血量等客观值兜底。
- 基础防护:登录 IP 限流、每连接令牌桶、房间数上限、操作防刷屏(每帧指令上限)。
六、可观测性
- 日志分层(Info/Warn/Error 分文件)+ 关键路径打点([RECV]/[SEND] 联调期必备)。
- 异常日志要带完整堆栈——只记
e.Message的 catch 等于没记(TargetInvocationException 的 Message 永远是那句废话)。 - 无头冒烟脚本(模拟客户端按协议发包)能不开客户端就回归整条链路,迭代效率神器。
一句话总结:服务端的功夫全在" boring 的地方"——回包纪律、线程模型、缓存异常留痕、限流背压。这些做到位,上层玩法才玩得起来。