KCP 实现可靠传输需手动管理重传、去重、滑动窗口和拥塞控制;必须定时调用 ikcp_update,ikcp_input 只接受原始 UDP 包,实时场景须启用 ikcp_nodelay。

为什么直接用 UDP 写可靠传输几乎必踩坑
因为 UDP 本身不保证送达、不保序、不控速,而“可靠”意味着你要自己实现重传、去重、滑动窗口、拥塞控制——这些加起来就是半个 TCP。KCP 的价值不是“让 UDP 变成 TCP”,而是提供一套**可调、轻量、用户态可控**的可靠传输逻辑。它不替换 socket,只是套在 sendto/recvfrom 外面做状态管理。
常见错误现象:KCP 对象没做 ikcp_update 定时驱动 → 数据发出去就消失;ikcp_input 被调用时传入了乱序或重复的 UDP 包 → 内部状态错乱;没设 ikcp_nodelay 却期望低延迟 → 默认 100ms 间隔导致卡顿。
- 必须每帧(或固定间隔,如 10ms)调用
ikcp_update,否则内部定时器不走,重传不触发 -
ikcp_input只能喂给它从同一个远端地址收上来的原始 UDP 数据包,不能拼接、不能解密后再喂(除非你也在 KCP 外层统一加解密) - 若用于实时对战或语音,务必开
ikcp_nodelay(kcp, 1, 10, 2, 1):启用快速模式、设置内部 flush 间隔为 10ms、2 次 ACK 触发快速重传、关闭流控
如何把 KCP 嵌进现有 UDP socket 流程
核心是拦截收发路径:UDP socket 收到原始数据后,先交给 ikcp_input 解包;要发业务数据时,调用 ikcp_send,再把 KCP 输出的待发送数据用 sendto 发出去。
使用场景:已有基于 epoll/select 的 UDP server,不想改事件循环结构。
- 每个连接(即每个远端 IP:port)需独立分配一个
ikcp_cb回调和ikcpcb*实例,不能多个连接共用一个 KCP 对象 -
ikcp_create的 conv 参数必须双方一致,且最好由业务层生成(比如用 client ID 哈希),不要硬写 0x1234 - 每次
ikcp_send后,必须轮询ikcp_flush—— 它会把待发数据拷贝进你提供的缓冲区,再由你调用sendto发出;不调flush就等于没发
wndsize
- 服务端建议设
ikcp_wndsize(kcp, 128, 128),客户端可略大(如 256),但别超过 MTU 的整数倍(避免 UDP 分片) - 内网环境可设
ikcp_setmtu(kcp, 1400),公网建议 1200 或更低,避开多数路由器的分片阈值 - 重传超时基线(
rx_minrto)默认 100ms,高丢包 Wi-Fi 下建议压到 30–50ms;但低于 20ms 容易引发误重传,尤其在 jitter 大的链路
加密和多路复用必须自己做
KCP 不处理应用层协议,也不管你是不是一包多条消息。它只保证“这个字节流按序、不丢地到达”,其余全是你的事。
容易被忽略的地方:KCP 输出的待发送数据可能跨多个 UDP 包(尤其大消息 + 小 MTU),而 ikcp_send 输入的是完整业务消息;如果你在 KCP 上层做了 TLS 或自定义加密,必须确保加密/解密发生在 KCP 的输入/输出边界之外。
- 不要对 KCP 内部数据结构做 memcpy 或 reinterpret_cast —— 它的内存布局不公开,只通过 API 访问
- 多个逻辑 channel(如信令 + 音频 + 控制)不能靠 KCP 自动分离,得在
ikcp_send前加 2 字节 type header,接收端用ikcp_recv后按 header 分发 - 如果用了
SO_REUSEPORT多进程 UDP server,每个进程必须维护自己的 KCP 实例池,不能共享 fd 或 kcp 对象
最常漏掉的一点:KCP 的 conv 是会话标识,但它不校验对方是否“配对”。如果 client 用 conv=1 连 server,server 却用 conv=2 初始化 KCP,双方能建连、能收发,但重传和窗口完全错位——现象是前几秒正常,之后吞吐断崖下跌,debug 极难定位。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











