年轻人还记得KCP吗?什么是KCP,怎么使用呢!!!

一、什么是KCP

KCP是一种网络传输协议(A Fast and Reliable ARQ Protocol),可以视它为TCP的代替品,但是它运行于用户空间,它不管底层的发送与接收,只是个纯算法实现可靠传输,它的特点是牺牲带宽来降低延迟。因为TCP协议的大公无私,经常牺牲自己速度来减少网络拥塞,它是从大局上考虑的。而KCP是自私的,它只顾自己的传输效率,从不管整个网络的拥塞情况。举个例子,TCP检测到丢包的时候,首先想到的是网络拥塞了,要放慢自己的速度别让网络更糟,而KCP想到的赶紧重传别耽误事。

TCP的特点是可靠传输(累积确认、超时重传、选择确认)、流量控制(滑动窗口)、拥塞控制(慢开始、拥塞避免、快重传、快恢复)、面向连接。KCP对这些参数基本都可配,也没用建立/关闭连接的过程。

其实KCP并不神秘,因为TCP的高度自治(很多东西都不可配),满足不了如今各种速度需求。而KCP就是基于UDP协议,再将一些TCP经典的机制移植过来,变成参数可配。

二、技术特性

  • RTO翻倍vs不翻倍:TCP超时计算是RTOx2,这样连续丢三次包就变成RTOx8了,十分恐怖,而KCP启动快速模式后不x2,只是x1.5(实验证明1.5这个值相对比较好),提高了传输速度。
  • 选择性重传 vs 全部重传:TCP丢包时会全部重传从丢的那个包开始以后的数据,KCP是选择性重传,只重传真正丢失的数据包。
  • 快速重传:发送端发送了1,2,3,4,5几个包,然后收到远端的ACK: 1, 3, 4, 5,当收到ACK3时,KCP知道2被跳过1次,收到ACK4时,知道2被跳过了2次,此时可以认为2号丢失,不用等超时,直接重传2号包,大大改善了丢包时的传输速度。
  • 延迟ACK vs 非延迟ACK :TCP为了充分利用带宽,延迟发送ACK(NODELAY都没用),这样超时计算会算出较大RTT时间,延长了丢包时的判断过程。KCP的ACK是否延迟发送可以调节。
  • UNA vs ACK+UNA :ARQ模型响应有两种,UNA(此编号前所有包已收到,如TCP)和ACK(该编号包已收到),KCP有单独ACK,且数据包和ACK包都带UNA信息,有效降低ACK丢失成本。
  • 非退让流控:KCP正常模式同TCP一样使用公平退让法则,即发送窗口大小由:发送缓存大小、接收端剩余接收缓存大小、丢包退让及慢启动这四要素决定。但传送及时性要求很高的小数据时,可选择通过配置跳过后两步,仅用前两项来控制发送频率。以牺牲部分公平性及带宽利用率之代价,换取了开着BT都能流畅传输的效果。

年轻人还记得KCP吗?什么是KCP,怎么使用呢!!!_第1张图片
年轻人还记得KCP吗?什么是KCP,怎么使用呢!!!_第2张图片
还等什么小编推荐自己的linuxC/C++语言交流群:【1106675687】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!前100名进群领取,额外赠送一份价值199的C/C++、linux资料包含(视频教程、电子书、实战项目及代码)

三、怎么使用

KCP只有两个文件,分别是ikcp.c和ikcp.h,代码行数1300左右。使用KCP和使用TCP有些不同,所以上手之前需要先了解下KCP如何使用,需要时间成本。

第一步,就是创建一个kcp实例,相当于一个句柄。

ikcpcb* ikcp_create(IUINT32 conv, void *user)

第二步,设置发送数据的接口,底层用哪种socket都没问题,只要能把数据发送出去,建议使用UDP,比较简单。

int output(const char *buf, int len, ikcpcb *kcp, void *user)

第三步,更新KCP状态。KCP运行于用户空间,所以需要手动去更新每个实例的状态,其实主要就是检测哪些数据包该重传了。

void ikcp_update(ikcpcb *kcp, IUINT32 current)

第四步,发送数据。调用ikcp_send之后,KCP最后会使用上面设置的output函数来将发送数据(KCP自己并不关心如何发送数据)。

int ikcp_send(ikcpcb *kcp, const char *buffer, int len)

第五步,预接收数据。先手动预接收数据,然后再调用ikcp_input将裸数据交给KCP,这些数据有可能是KCP控制报文,并不是我们要的数据。

int ikcp_input(ikcpcb *kcp, const char *data, long size)

第六步,接收数据。此时收到的数据才是真正的数据,重组操作在调用ikcp_recv之前就完成了。

int ikcp_recv(ikcpcb *kcp, char *buffer, int len)

总体上还是容易理解的,以前我们是直接使用各种socket和对端通信,各种功能由自己控制。现在是在socket之上使用了一个中间件KCP,帮忙实现快速可靠传输功能。注意一下KCP有模式的区分,不同模式下的速度表现不一样,建议把参数配好之后再使用,否则使用的都是默认的参数。

四、协议配置

协议默认模式是一个标准的 ARQ,需要通过配置打开各项加速开关:

工作模式

int ikcp_nodelay(ikcpcb *kcp,int nodelay,int interval,int resend,int nc);

  • nodelay :是否启用 nodelay模式,0不启用;1启用。
  • interval :协议内部工作的 interval,单位毫秒,比如 10ms或者 20ms
  • resend :快速重传模式,默认0关闭,可以设置2(2次ACK跨越将会直接重传)
  • nc :是否关闭流控,默认是0代表不关闭,1带包关闭。

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

最大窗口:int ikcp_wndsize(ikcpcb *kcp,int sndwnd,int rcvwnd);该调用将会设置协议的最大发送窗口和最大接收窗口大小,默认为32。

最大传输单元:纯算法协议并不负责探测 MTU,默认 mtu是1400字节,可以使用 ikcp_setmtu来设置该值。该值将会影响数据包归并及分片时候的最大传输单元。

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

内存分配器
默认KCP协议使用 malloc/free进行内存分配释放,如果应用层接管了内存分配,可以用ikcp_allocator来设置新的内存分配器,注意要在一开始设置:ikcp_allocator(my_new_malloc, my_new_free);

前向纠错注意
为了进一步提高传输速度,下层协议也许会使用前向纠错技术。需要注意,前向纠错会根据冗余信息解出原始数据包。相同的原始数据包不要两次input到KCP,否则将会导致kcp以为对方重发了,这样会产生更多的ack占用额外带宽。

比如下层协议使用最简单的冗余包:单个数据包除了自己外,还会重复存储一次上一个数据包,以及上上一个数据包的内容:

Fn=(Pn,Pn-1,Pn-2)
P0 =(0, X, X)
P1 =(1,0, X)
P2 =(2,1,0)
P3 =(3,2,1)

这样几个包发送出去,接收方对于单个原始包都可能被解出3次来(后面两个包任然会重复该包内容),那么这里需要记录一下,一个下层数据包只会input给kcp一次,避免过多重复ack带来的浪费。

五、快在哪里

  • 没用使用任何系统调用接口
  • 无需建立/关闭连接(就KCP本身来说)
  • 很多影响速度的参数都可配

六、使用场景

丢包率高的网络环境下KCP的优点才会显示出来。如果不丢包,那么TCP和KCP的效率不会差别很大,可能就是少了连接建立/关闭而已。一般来讲,在公网上传输的都可以使用,特别是对实时性要求较高的程序,如LOL。

七、有何缺点

学习成本
据说有些运营商对UDP有限制?

你可能感兴趣的:(网络,c++,java,linux,python)