一 KCP

KCP是什么

kcp是由韦一笑开发的一个开源项目

KCP是一个快速可靠协议,能以比 TCP 浪费 10%-20% 的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。纯算法实现,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据包的发送方式,以 callback的方式提供给 KCP。 连时钟都需要外部传递进来,内部不会有任何一次系统调用。

特性

tcp面向吞吐,最大化利用带宽
kcp面向速度,快速响应
翻了一下文档,kcp所有的优化都是围绕重传进行的

rto优化

tcp中的rto使用来用控制超时重传时间用的,具体细节可以参考这两篇文章TCP/IP之RTO、RTT TCP/IP重传超时--RTO

TCP超时计算是RTOx2,这样连续丢三次包就变成RTOx8了,十分恐怖,而KCP启动快速模式后不x2,只是x1.5(实验证明1.5这个值相对比较好),提高了传输速度。

通过对rto的优化减少重传等待时间

选择重传

TCP丢包时会全部重传从丢的那个包开始以后的数据,KCP是选择性重传,只重传真正丢失的数据包。

减少重传数量

快速重传

发送端发送了1,2,3,4,5几个包,然后收到远端的ACK: 1, 3, 4, 5,当收到ACK3时,KCP知道2被跳过1次,收到ACK4时,知道2被跳过了2次,此时可以认为2号丢失,不用等超时,直接重传2号包,大大改善了丢包时的传输速度。

减少重传等待时间

ack优化

TCP为了充分利用带宽,延迟发送ACK(NODELAY都没用),这样超时计算会算出较大 RTT时间,延长了丢包时的判断过程。KCP的ACK是否延迟发送可以调节。

参考tcp_nodelay介绍

减少重传等待时间

arq优化

ARQ模型响应有两种,UNA(此编号前所有包已收到,如TCP)和ACK(该编号包已收到),光用UNA将导致全部重传,光用ACK则丢失成本太高,以往协议都是二选其一,而 KCP协议中,除去单独的 ACK包外,所有包都有UNA信息。

ACK+UNA这种方案是为了实现上面的特性,比如如何让发送端知道丢包,进行快速重传。

非退让流控

KCP正常模式同TCP一样使用公平退让法则,即发送窗口大小由:发送缓存大小、接收端剩余接收缓存大小、丢包退让及慢启动这四要素决定。但传送及时性要求很高的小数据时,可选择通过配置跳过后两步,仅用前两项来控制发送频率。以牺牲部分公平性及带宽利用率之代价,换取了开着BT都能流畅传输的效果。

可以先了解下Tcp的拥塞窗口tcp之拥塞窗口 。 而kcp为了速度,可以禁用退让和满启动等导致发送变慢(速度慢不一定吞吐慢,tcp通过各种机制平衡)的特性。

其他参数

参考kcp基本使用手册

mtu

udp传输必然会碰到拆包组包的问题,那么单个包大小多小合适呢,这里可以参考一下UDP中一个包的大小最大能多大?
KCP中默认的包大小是1400,这个配置用在局域网压测比较合适,实际情况可以适当缩小。

窗口大小

作用类似tcp的发送和接收窗口默认32
参考 kcp的流控文档

最小RTO

不管是 TCP还是 KCP计算 RTO时都有最小 RTO的限制,即便计算出来RTO为40ms,由于默认的 RTO是100ms,协议只有在100ms后才能检测到丢包,快速模式下为30ms,可以手动更改该值:

rto的计算方式参考TCP/IP重传超时--RTO

使用参数

  • nodelay:控制rto,如果启动会在各个环节减少rto时间
  • interval :控制内部逻辑,主要是缓冲区的flash
  • resend:快速重传模式,默认0关闭,可以设置2(2次ACK跨越将会直接重传)
  • nc:是否关闭流控,默认是0代表不关闭,1代表关闭。

常用参数模式

  1. 普通模式:ikcp_nodelay(kcp, 0, 40, 0, 0);
  2. 极速模式:ikcp_nodelay(kcp, 1, 10, 2, 1);

二 FEC

什么是FEC

谈谈网络通信中的 FEC 基础

编码原理

Reed-Solomon纠删码简析

三 如何在KCP中使用FEC纠错

kcp的使用

kcp可以理解为一套协议栈,拥有拆包,组包,流控等功能,但是本身不具备收发能力。
因此业务上需要

  1. 自行创建套接字
  2. 收发不可靠数据(可能是无序的,可能是丢包的)
  3. 把数据送给kcp协议栈
  4. 通过kcp协议栈获得可靠数据(有序且完整)

kcp + fec

通过上文可以知道,kcp做了很多事情,虽然kcp对丢包超时等情况做了大量优化,但是在一些实时较高的场景(比如游戏)还是难以接受。此时fec就可以和kcp一起使用,利用fec的纠错机制进一步减少重传。
通过kcp的使用流程可知,fec最适合嵌入的位置是2和3之间,如果在把数据送入协议栈之前通过纠错,修复丢包的数据,那么协议栈就会认为数据是完整的,就不会要求发送端重传。

实现

目前已经有将kcp和fec融合的实现
c版本 https://github.com/xtaci/libkcp
go版本 https://github.com/xtaci/kcp-go

四 参数与性能

todo


烨烨烨
1 声望0 粉丝

test