9.5 KiB
9.5 KiB
KCP 网络底层调整 TODO
目标
将当前项目的网络底层从“自写可靠 UDP + ACK/重传/会话”调整为“UDP 承载 KCP + 明确的传输层/会话层/消息层/同步层分层”,避免职责重叠,并为后续的同步优化、重连、监控打基础。
当前现状
Assets/Scripts/Network/NetworkTransport/ReliableUdpTransport.cs已经引入Kcp-CSharp.dll,但主体逻辑仍然是自定义可靠 UDP。- 当前传输层仍保留以下逻辑:
- 自定义
Packet - 自定义 ACK
- 自定义重传
- 自定义超时会话清理
- 自定义顺序交付
- 自定义
NetworkManager -> MessageManager -> ITransport的抽象还没有完全收口,接口和实现存在不一致。- 当前业务链路不是纯 RPC,而是:
- 登录 / 登出
- 心跳 / 对时
PlayerInput上行PlayerState下行- 本地预测 / 服务器校正
必须调整的内容
1. 收口传输层接口
统一 ITransport 的职责,避免上层绕过抽象调用不存在的方法。
建议接口至少包含:
StartAsync()Stop()Connect(...)或客户端构造时明确默认远端Send(byte[] data)SendTo(byte[] data, IPEndPoint target),仅服务端或特殊场景需要SendToAll(byte[] data),仅服务端广播需要OnReceiveOnConnectedOnDisconnectedOnError
当前要处理的问题:
MessageManager调用了transport.Send(...),但ITransport中没有定义该接口。ReliableUdpTransport当前的SendTo(Packet, IPEndPoint)与接口SendTo(byte[], IPEndPoint)不一致。
2. 用 KCP 替代自定义可靠 UDP
KCP 接入后,以下能力不应继续由项目侧重复实现:
- ACK 管理
- 重传调度
- 收发序号维护
- 有序交付
- 滑动窗口
因此需要删除或重构以下内容:
Assets/Scripts/Network/NetworkTransport/Packet.csAssets/Scripts/Network/NetworkTransport/ClientSession.cs中基于自定义 seq/ack 的逻辑Assets/Scripts/Network/NetworkTransport/ReliableUdpTransport.cs中的:CheckRetransmitHandleAckPacket- 自定义
PendingAcks - 自定义重复包 / 乱序包处理
- 自定义可靠性定时器
3. 重建会话层
KCP 模式下需要清晰区分:
- UDP Socket
- KCP Session
- 业务连接状态
建议设计:
- 客户端:
- 单一默认远端
- 单一
KcpSession
- 服务端:
- 按
IPEndPoint + conv管理多个KcpSession - 支持会话建立、心跳超时、断线清理
- 按
会话层至少需要管理:
conv- 远端地址
- 最后活跃时间
- KCP 实例
- 连接状态
- 断开原因
4. 将网络线程与 Unity 主线程解耦
当前 MessageManager.OnTransportReceiveAsync(...) 直接进入业务 handler,而后续 handler 会继续访问:
MasterManagerPlayerGameObjectUI
这些逻辑不应该直接在网络接收线程执行。
需要改为:
- 网络线程收包
- 解析最小必要信息
- 投递到线程安全队列
- 在 Unity
Update()中统一分发到业务层
建议新增:
MainThreadDispatcher- 或
ConcurrentQueue<Action> - 或
ConcurrentQueue<ReceivedEnvelope>
5. 重新划分消息 QoS
当前所有消息看起来都走同一种可靠传输语义,这对高频同步不合理。
建议至少拆成两类:
- 强可靠消息
- 登录
- 登出
- 房间管理
- 关键系统命令
- 高频同步消息
PlayerInputPlayerState- 以后可能的快照、插值状态、非关键位置更新
需要明确一个原则:
- 如果
PlayerState继续走可靠有序流,旧包阻塞会放大延迟。 - 如果
PlayerInput全部严格可靠发送,也可能产生输入堆积。
这部分要结合项目玩法决定:
- 方案 A:全部先走 KCP,先完成架构收口,再做同步优化
- 方案 B:命令消息走 KCP,同步消息走裸 UDP / 另一条轻量通道
短期建议先用方案 A 收口,后续再细分。
6. 重构连接生命周期
需要把“传输连接”与“业务登录状态”分开。
建议生命周期:
- 创建 UDP Socket
- 初始化 KCP
- 连接服务器 / 建立默认会话
- 开始收包循环和 KCP Update
- 发送
LoginRequest - 收到
LoginResponse后进入已登录状态 - 开始心跳与超时检测
- 超时或异常时触发断线回调
- 按需重连
不要再把“收到登录响应才知道默认服务器端点”这种逻辑和连接过程混在一起。
建议同步调整的内容
1. 对时与发送频率分离
当前 MovementComponent 通过修改 _sendInterval 来追赶服务器 Tick,这会把:
- 时钟校正
- 发包频率
- 同步稳定性
绑在一起。
建议改为:
- 固定输入发送频率
- 单独维护客户端与服务端 Tick 偏移
- 在校正阶段使用 replay / reconcile,而不是直接依赖发包间隔漂移
2. 增加 KCP 参数配置入口
建议支持配置以下参数:
NoDelayIntervalResendNCSndWndRcvWndMTUDeadLink
建议做法:
- 新增
KcpTransportConfig - 客户端和服务端分别可配置
- 支持 Inspector 或 ScriptableObject 配置
3. 增加网络观测指标
至少需要输出:
- RTT
- 重传次数
- 待发送队列长度
- 待接收队列长度
- 会话数量
- 最后活跃时间
- 超时断线原因
后续排查“卡顿、抖动、延迟尖刺、重连失败”时会用到。
4. 明确服务端广播策略
当前 SendToAll 是直接遍历会话广播。接入 KCP 后要明确:
- 广播是否逐会话独立写入
- 广播时是否允许慢连接拖累整体发送
- 广播消息是否需要按类型分优先级
推荐实施步骤
阶段 1:先把抽象收口
- 调整
ITransport,补齐上层真正需要的发送与连接接口。 - 让
MessageManager只依赖ITransport暴露的方法,不再假设具体实现细节。 - 修复当前
ReliableUdpTransport与ITransport的方法签名不一致问题。 - 让网络层先达到“接口一致、结构可替换”的状态。
交付标准:
- 上层不再直接依赖某个具体 Transport 的额外方法。
- 业务层不关心底层是 UDP 还是 KCP。
阶段 2:引入 KcpTransport
- 新建
KcpTransport,不要在旧的ReliableUdpTransport上继续打补丁。 - 用
UdpClient只负责收发原始 UDP 数据报。 - 每个连接维护一个
KcpSession。 - UDP 收包后先交给对应的 KCP 实例
Input。 - 周期性驱动 KCP
Update/Check。 - 从 KCP
Recv中取出完整业务消息后再触发OnReceive。
交付标准:
- 传输层不再维护自定义 ACK/重传逻辑。
- KCP 可以完成完整收发。
阶段 3:移除旧可靠 UDP 结构
- 删除或废弃
Packet.cs。 - 删除或废弃旧
ClientSession中基于 seq/ack 的缓存和重传代码。 - 删除
ReliableUdpTransport中的:- retransmit timer
- ack handler
- packet seq 交付逻辑
- 保留必要的会话容器与连接生命周期管理。
交付标准:
- 项目中不再同时存在“两套可靠性机制”。
阶段 4:主线程分发改造
- 新增线程安全接收队列。
- 网络线程只负责:
- 收包
- KCP 输入输出
- 基础错误处理
- Unity 主线程负责:
- 消息分发
- 游戏对象修改
- UI 更新
交付标准:
- 网络消息不会直接在非主线程操作 Unity 对象。
阶段 5:连接与心跳改造
- 明确“连接成功”和“登录成功”是两个不同状态。
- 心跳只承担:
- 存活检测
- RTT / 时间同步
- 会话超时和断线重连逻辑放在 session manager,而不是业务消息处理里。
交付标准:
- 断线、超时、登录失败、重连等状态可以被明确区分。
阶段 6:同步策略优化
- 重新评估
PlayerInput是否必须严格可靠。 - 重新评估
PlayerState是否应使用可靠有序流。 - 调整客户端预测、回滚、纠正策略。
- 把对时逻辑从
_sendInterval漂移中拆出来。
交付标准:
- 高频同步场景下不会因为旧包阻塞导致位置明显滞后。
阶段 7:监控与调试工具补齐
- 打印会话状态。
- 输出 RTT、发送队列、丢包、重传等指标。
- 提供调试开关,避免正式环境日志过多。
交付标准:
- 网络问题可以通过日志和指标定位。
推荐新增的结构
建议新增或重命名以下模块:
Assets/Scripts/Network/NetworkTransport/KcpTransport.csAssets/Scripts/Network/NetworkTransport/KcpSession.csAssets/Scripts/Network/NetworkTransport/KcpTransportConfig.csAssets/Scripts/Network/NetworkApplication/MainThreadNetworkDispatcher.cs
建议废弃或重构:
Assets/Scripts/Network/NetworkTransport/ReliableUdpTransport.csAssets/Scripts/Network/NetworkTransport/ClientSession.csAssets/Scripts/Network/NetworkTransport/Packet.cs
验收标准
- 上层只依赖统一的
ITransport。 - 传输层不再重复实现 ACK / 重传 / 顺序控制。
- 客户端与服务端都能正常建立 KCP 会话。
- 登录、心跳、输入、状态同步链路可正常跑通。
- 非主线程不再直接访问 Unity 对象。
- 会话超时、断线、重连有明确状态与日志。
- 高频移动同步在丢包 / 抖动场景下仍可用。
备注
- 当前最不建议的做法,是在现有
ReliableUdpTransport上继续叠加更多 KCP 相关判断。这样会让自定义可靠 UDP 和 KCP 职责长期重叠。 - 正确方向是:先抽象收口,再用新的
KcpTransport替换旧实现。