这部分摘自https://github.com/skywind3000/kcp,这是源码的官方网站,有一些使用介绍,其他各种衍生版本,在实际中的使用情况以及一些测试比较分析。
KCP是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。纯算法实现,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据包的发送方式,以 callback的方式提供给 KCP。 连时钟都需要外部传递进来,内部不会有任何一次系统调用。
整个协议只有 ikcp.h,ikcp.c两个源文件,可以方便的集成到用户自己的协议栈中。也许你实现了一个P2P,或者某个基于 UDP的协议,而缺乏一套完善的ARQ可靠协议实现,那么简单的拷贝这两个文件到现有项目中,稍微编写两行代码,即可使用。
TCP是为流量设计的(每秒内可以传输多少KB的数据),讲究的是充分利用带宽。而 KCP是为流速设计的(单个数据包从一端发送到一端需要多少时间),以10%-20%带宽浪费的代价换取了比 TCP快30%-40%的传输速度。TCP信道是一条流速很慢,但每秒流量很大的大运河,而KCP是水流湍急的小激流。KCP有正常模式和快速模式两种,通过以下策略达到提高流速的结果:
>TCP超时计算是RTOx2,这样连续丢三次包就变成RTOx8了,十分恐怖,而KCP启动快速模式后不x2,只是x1.5(实验证明1.5这个值相对比较好),提高了传输速度
TCP丢包时会全部重传从丢的那个包开始以后的数据,KCP是选择性重传,只重传真正丢失的数据包。(TCP同样有选择重传SACK,但有区别,后续文章再介绍)。
发送端发送了1,2,3,4,5几个包,然后收到远端的ACK: 1,3,4,5,当收到ACK3时,KCP知道2被跳过1次,收到ACK4时,知道2被跳过了2次,此时可以认为2号丢失,不用等超时,直接重传2号包,大大改善了丢包时的传输速度。
TCP为了充分利用带宽,延迟发送ACK(NODELAY都没用),这样超时计算会算出较大 RTT时间,延长了丢包时的判断过程。KCP的ACK是否延迟发送可以调节。
ARQ模型响应有两种,UNA(此编号前所有包已收到,如TCP)和ACK(该编号包已收到),光用UNA将导致全部重传,光用ACK则丢失成本太高,以往协议都是二选其一,而 KCP协议中,除去单独的 ACK包外,所有包都有UNA信息。
KCP正常模式同TCP一样使用公平退让法则,即发送窗口大小由:发送缓存大小、接收端剩余接收缓存大小、丢包退让及慢启动这四要素决定。但传送及时性要求很高的小数据时,可选择通过配置跳过后两步,仅用前两项来控制发送频率。以牺牲部分公平性及带宽利用率之代价,换取了开着BT都能流畅传输的效果
KCP协议是一个纯粹的ARQ协议,通过重传机制实现UDP数据包的可靠传输,在整个网络结构中的位置如下图所示。
单纯的ARQ在实际使用中并不能满足所有的网络场景,特别是网络拥塞时,大量的重传会导致更多的丢包。增加FEC是一个明智的选择,在KCP协议中,也并不排斥在KCP上增加FEC。但是需要注意的是,FEC加重传可能导致数据包的时延与抖动,同时,如果FEC解码得到的包经由重传或者网络延迟到达,需要在应用层进行检测,避免大量重复包影响KCP的传输效率。
KCP通过ikcp_create 创建一个KCP对象,每个不同的会话将产生不同的对象。因为KCP协议本身并没有提供网络部分的代码,所以需要将UDP发送函数的回调设置到KCP中,在有需要时,调用回调函数即可。KCP也支持外部的内存分配与日志回调,为用户提供了非常充分的自由度。
整个KCP协议主要依靠一个循环ikcp_update来驱动整个算法的运转,所有的数据发送,接收,状态变化都依赖于此,所以如果有操作占用每一次update的周期过长,或者设置内部刷新的时间间隔过大,都会导致整个算法的效率降低。在ikcp_update中最终调用的是ikcp_flush,这是协议中的一个核心函数,将数据,确认包,以及窗口探测和应答发送到对端。
KCP使用ikcp_send发送数据,该函数调用ikcp_output发送数据,实际上最终调用事先注册的发送回调发送数据。KCP通过ikcp_recv将数据接收出来,如果被分片发送,将在此自动重组,数据将与发送前保持一致。
KCP中只有一种数据包格式,不管是数据还是信令,都使用相同的结构与头。KCP头中每一个字段的意义如下:
为一个表示会话编号的整数,和TCP的 conv一样,通信双方需保证 conv相同,相互的数据包才能够被接受。conv唯一标识一个会话,但通信双方可以同时存在多个会话。
用来区分分片的作用。IKCP_CMD_PUSH:数据分片;IKCP_CMD_ACK:ack分片; IKCP_CMD_WASK:请求告知窗口大小;IKCP_CMD_WINS:告知窗口大小。
用户数据可能会被分成多个KCP包发送,frag标识segment分片ID(在message中的索引,由大到小,0表示最后一个分片)。
剩余接收窗口大小(接收窗口大小-接收队列大小),发送方的发送窗口不能超过接收方给出的数值。
message发送时刻的时间戳
message分片segment的序号,按1累次递增。
待接收消息序号(接收滑动窗口左端)。对于未丢包的网络来说,una是下一个可接收的序号,如收到sn=10的包,una为11。
数据长度。
在结构体IKCPSEG中,除了上述的包结构的字段外,还定义了几个非常重要的变量:
下次超时重传的时间戳。
该分片的超时重传等待时间,其计算方法同TCP。
收到ack时计算的该分片被跳过的累计次数,此字段用于快速重传,自定义需要几次确认开始快速重传。
发送分片的次数,每发送一次加一。发送的次数对RTO的计算有影响,但是比TCP来说,影响会小一些,计算思想类似
IKCPCB是KCP中最重要的结构,也是在会话开始就创建的对象,代表着这次会话,所以这个结构体体现了一个会话所需要涉及到的所有组件。其中一些参数在IKCPSEG中已经描述,不再多说。
https://wetest.qq.com/lab/view/391.html?=content_qcloud
https://blog.csdn.net/qq_36748278/article/details/80171575
https://blog.csdn.net/sai_j/article/details/82598692
http://kaiyuan.me/2017/07/29/KCP%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/
https://www.cnblogs.com/yuanyifei1/p/6837453.html