简介

这部分摘自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有正常模式和快速模式两种,通过以下策略达到提高流速的结果:

  • RTO翻倍vs不翻倍:

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

  • 选择性重传 vs 全部重传:

TCP丢包时会全部重传从丢的那个包开始以后的数据,KCP是选择性重传,只重传真正丢失的数据包。(TCP同样有选择重传SACK,但有区别,后续文章再介绍)。

  • 快速重传:

发送端发送了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(该编号包已收到),光用UNA将导致全部重传,光用ACK则丢失成本太高,以往协议都是二选其一,而 KCP协议中,除去单独的 ACK包外,所有包都有UNA信息。

  • 非退让流控:

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

设计思想

KCP协议是一个纯粹的ARQ协议,通过重传机制实现UDP数据包的可靠传输,在整个网络结构中的位置如下图所示。

图 1 KCP 在网络体系中位置

单纯的ARQ在实际使用中并不能满足所有的网络场景,特别是网络拥塞时,大量的重传会导致更多的丢包。增加FEC是一个明智的选择,在KCP协议中,也并不排斥在KCP上增加FEC。但是需要注意的是,FEC加重传可能导致数据包的时延与抖动,同时,如果FEC解码得到的包经由重传或者网络延迟到达,需要在应用层进行检测,避免大量重复包影响KCP的传输效率。

图 2 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包与IKCPSEG

图3 KCP 报文结构

KCP中只有一种数据包格式,不管是数据还是信令,都使用相同的结构与头。KCP头中每一个字段的意义如下:

  • Conv,32bit,4Byte

为一个表示会话编号的整数,和TCP的 conv一样,通信双方需保证 conv相同,相互的数据包才能够被接受。conv唯一标识一个会话,但通信双方可以同时存在多个会话。

  • cmd,8bit,1Byte

用来区分分片的作用。IKCP_CMD_PUSH:数据分片;IKCP_CMD_ACK:ack分片; IKCP_CMD_WASK:请求告知窗口大小;IKCP_CMD_WINS:告知窗口大小。

  • frag,8bit,1Byte

用户数据可能会被分成多个KCP包发送,frag标识segment分片ID(在message中的索引,由大到小,0表示最后一个分片)。

  • wnd,16bit,2Byte

剩余接收窗口大小(接收窗口大小-接收队列大小),发送方的发送窗口不能超过接收方给出的数值。

  • ts,32bit,4Byte

message发送时刻的时间戳

  • sn,32bit,4Byte

message分片segment的序号,按1累次递增。

  • una,32bit,4Byte

待接收消息序号(接收滑动窗口左端)。对于未丢包的网络来说,una是下一个可接收的序号,如收到sn=10的包,una为11。

  • len,32bit,4Byte

数据长度。

在结构体IKCPSEG中,除了上述的包结构的字段外,还定义了几个非常重要的变量:

  • resendts

下次超时重传的时间戳。

  • rto

该分片的超时重传等待时间,其计算方法同TCP。

  • fastack

收到ack时计算的该分片被跳过的累计次数,此字段用于快速重传,自定义需要几次确认开始快速重传。

  • xmit

发送分片的次数,每发送一次加一。发送的次数对RTO的计算有影响,但是比TCP来说,影响会小一些,计算思想类似

 IKCPCB结构

IKCPCB是KCP中最重要的结构,也是在会话开始就创建的对象,代表着这次会话,所以这个结构体体现了一个会话所需要涉及到的所有组件。其中一些参数在IKCPSEG中已经描述,不再多说。

  • conv:标识这个会话;
  • mtu:最大传输单元,默认数据为1400,最小为50;
  • mss:最大分片大小,不大于mtu;
  • state:连接状态(0xFFFFFFFF表示断开连接);
  • snd_una:第一个未确认的包;
  • snd_nxt:下一个待分配的包的序号;
  • rcv_nxt:待接收消息序号。为了保证包的顺序,接收方会维护一个接收窗口,接收窗口有一个起始序号rcv_nxt(待接收消息序号)以及尾序号 rcv_nxt + rcv_wnd(接收窗口大小);
  • ssthresh:拥塞窗口阈值,以包为单位(TCP以字节为单位);
  • rx_rttval:RTT的变化量,代表连接的抖动情况;
  • rx_srtt:smoothed round trip time,平滑后的RTT;
  • rx_rto:由ACK接收延迟计算出来的重传超时时间;
  • rx_minrto:最小重传超时时间;
  • snd_wnd:发送窗口大小;
  • rcv_wnd:接收窗口大小;
  • rmt_wnd:远端接收窗口大小;
  • cwnd:拥塞窗口大小;
  • probe:探查变量,IKCP_ASK_TELL表示告知远端窗口大小。IKCP_ASK_SEND表示请求远端告知窗口大小;
  • interval:内部flush刷新间隔,对系统循环效率有非常重要影响;
  • ts_flush:下次flush刷新时间戳;
  • xmit:发送segment的次数,当segment的xmit增加时,xmit增加(第一次或重传除外);
  • rcv_buf:接收消息的缓存;
  • nrcv_buf:接收缓存中消息数量;
  • snd_buf:发送消息的缓存;
  • nsnd_buf:发送缓存中消息数量;
  • rcv_queue:接收消息的队列
  • nrcv_que:接收队列中消息数量;
  • snd_queue:发送消息的队列;
  • nsnd_que:发送队列中消息数量;
  • nodelay:是否启动无延迟模式。无延迟模式rtomin将设置为0,拥塞控制不启动;
  • updated:是否调用过update函数的标识;
  • ts_probe:下次探查窗口的时间戳;
  • probe_wait:探查窗口需要等待的时间;
  • dead_link:最大重传次数,被认为连接中断;
  • incr:可发送的最大数据量;
  • acklist:待发送的ack列表;
  • ackcount:acklist中ack的数量,每个ack在acklist中存储ts,sn两个量;
  • ackblock:2的倍数,标识acklist最大可容纳的ack数量;
  • user:指针,可以任意放置代表用户的数据,也可以设置程序中需要传递的变量;
  • buffer:存储消息字节流;
  • fastresend:触发快速重传的重复ACK个数;
  • nocwnd:取消拥塞控制;
  • stream:是否采用流传输模式;
  • logmask:日志的类型,如IKCP_LOG_IN_DATA,方便调试;
  • output udp:发送消息的回调函数;
  • writelog:写日志的回调函数。

参考

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

KCP 协议与源码分析(一)相关推荐

  1. linux nDPI 协议检测 源码分析

    关于nDPI的基本功能就不在这介绍了,有兴趣了解的读者可以阅读官方的快速入门指南:https://github.com/ntop/nDPI/blob/dev/doc/nDPI_QuickStartGu ...

  2. 连续arq协议的利用率_KCP 协议与源码分析

    文章来源于 CSDN-专业IT技术社区-登录 简介 这部分摘自https://github.com/skywind3000/kcp,这是源码的官方网站,有一些使用介绍,其他各种衍生版本,在实际中的使用 ...

  3. Dubbo篇:基于Netty实现Dubbo协议编解码源码分析

    Dubbo协议解析 Dubbo协议设计参考了TCP/IP协议,包括协议头和协议体两部分.16字节报文头主要携带了魔法数(0xdabb,用于分割两个不同请求),以及当前请求报文是否是Request.Re ...

  4. PPP协议工作流程,结合ppp-2.4.9 源码分析

    ppp-2.4.9 源码分析 文章目录 ppp-2.4.9 源码分析 PPP协议工作流程 ppp-2.4.9 源码分析 全局变量和结构体说明 第一阶段 初始化 第二阶段 开始链接 第三阶段 建立PPP ...

  5. NEO源码分析之UTXO全局资产

    作者:廖京辉 原文链接:https://mp.weixin.qq.com/s?__biz=MzUzNDQwNDQ0Mw==&mid=2247483941&idx=1&sn=4a ...

  6. wireshark协议解析器 源码分析 封装调用

    源码分析 Wireshark启动时,所有解析器进行初始化和注册.要注册的信息包括协议名称.各个字段的信息.过滤用的关键字.要关联的下层协议与端口(handoff)等.在解析过程,每个解析器负责解析自己 ...

  7. FPGA学习之路—接口(2)—I2C协议详解+Verilog源码分析

    FPGA学习之路--I2C协议详解+Verilog源码分析 定义 I2C Bus(Inter-Integrated Circuit Bus) 最早是由Philips半导体(现被NXP收购)开发的两线时 ...

  8. NEO从源码分析看共识协议

    2019独角兽企业重金招聘Python工程师标准>>> 0x00 概论 不同于比特币使用的工作量证明(PoW)来实现共识,NEO提出了DBFT共识算法.DBFT改良自股权证明算法(P ...

  9. 20210803:AXI-Stream协议源码分析初探

    AXI-Stream协议细读 简要说明 交互握手过程简述 源码分析 TestBench测试 写在最后 参考资料 简要说明 不同于AXI和AXI-lite协议,AXI-Stream协议不是基于地址的数据 ...

最新文章

  1. EXP6 信息搜集与漏洞扫描
  2. Java常用的集合类
  3. Python网络_TCP/IP简介
  4. 3_6 CommandMode 命令模式
  5. Python 简单入门学习笔记
  6. java移动端接口测试_走进Java接口测试之测试框架TestNG数据驱动(入门篇)
  7. ExtJS+DWR+Spring+Hibernate开发HRMS(3)
  8. VC下使用Proc连接Oracle数据库
  9. Thinkphp5之ajax分页实现_paginate()参数详细
  10. MR Mapper类
  11. nis+nfs+autofs
  12. 机器学习中性能评估指标中的准确率(Accuracy)、召回率(Recall=TPR)、精确率(Precision)、误报率(FPR)、漏报率(FNR)及其关系
  13. 求一元硬币兑换成1分、2分、5分的硬币的兑换方案
  14. 2022年北京首店补贴及首店首发项目征集申报情况介绍,补贴200万
  15. UCASE() 函数
  16. 实战篇-通用的页面列表导出Excel控件
  17. docker入门_Docker入门
  18. 没有同理心_在设计上超越同理心
  19. 小黑算法成长日记8:最优多边形环状运算(加法和乘法)
  20. 基于html的网页设计-音乐主题

热门文章

  1. mre下的控件实现(一、公共的宏定义)
  2. 我所遇到最牛的sshd远程连接
  3. 5款热门可视化工具对比,老板直呼酷炫!
  4. 你们期待已久的微信功能,终于要来了
  5. springboot+mybatis plus+code generate+mysql + swagger ui简单demo
  6. 2022哪款充电宝质量好?2022充电宝排行
  7. unity超级马里奥2d游戏开发-03瓦片地图场景搭建
  8. 云计算机下erp的发展,云计算下企业ERP信息管理系统的应用
  9. 解决** (ArgumentError) argument error (stdlib) io_lib.erl:187: :io_lib.format(‘ * effective user\‘
  10. CTF_StegSolve使用方法