KCP 协议与源码分析(一)
简介
这部分摘自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数据包的可靠传输,在整个网络结构中的位置如下图所示。
单纯的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包与IKCPSEG
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 协议与源码分析(一)相关推荐
- linux nDPI 协议检测 源码分析
关于nDPI的基本功能就不在这介绍了,有兴趣了解的读者可以阅读官方的快速入门指南:https://github.com/ntop/nDPI/blob/dev/doc/nDPI_QuickStartGu ...
- 连续arq协议的利用率_KCP 协议与源码分析
文章来源于 CSDN-专业IT技术社区-登录 简介 这部分摘自https://github.com/skywind3000/kcp,这是源码的官方网站,有一些使用介绍,其他各种衍生版本,在实际中的使用 ...
- Dubbo篇:基于Netty实现Dubbo协议编解码源码分析
Dubbo协议解析 Dubbo协议设计参考了TCP/IP协议,包括协议头和协议体两部分.16字节报文头主要携带了魔法数(0xdabb,用于分割两个不同请求),以及当前请求报文是否是Request.Re ...
- PPP协议工作流程,结合ppp-2.4.9 源码分析
ppp-2.4.9 源码分析 文章目录 ppp-2.4.9 源码分析 PPP协议工作流程 ppp-2.4.9 源码分析 全局变量和结构体说明 第一阶段 初始化 第二阶段 开始链接 第三阶段 建立PPP ...
- NEO源码分析之UTXO全局资产
作者:廖京辉 原文链接:https://mp.weixin.qq.com/s?__biz=MzUzNDQwNDQ0Mw==&mid=2247483941&idx=1&sn=4a ...
- wireshark协议解析器 源码分析 封装调用
源码分析 Wireshark启动时,所有解析器进行初始化和注册.要注册的信息包括协议名称.各个字段的信息.过滤用的关键字.要关联的下层协议与端口(handoff)等.在解析过程,每个解析器负责解析自己 ...
- FPGA学习之路—接口(2)—I2C协议详解+Verilog源码分析
FPGA学习之路--I2C协议详解+Verilog源码分析 定义 I2C Bus(Inter-Integrated Circuit Bus) 最早是由Philips半导体(现被NXP收购)开发的两线时 ...
- NEO从源码分析看共识协议
2019独角兽企业重金招聘Python工程师标准>>> 0x00 概论 不同于比特币使用的工作量证明(PoW)来实现共识,NEO提出了DBFT共识算法.DBFT改良自股权证明算法(P ...
- 20210803:AXI-Stream协议源码分析初探
AXI-Stream协议细读 简要说明 交互握手过程简述 源码分析 TestBench测试 写在最后 参考资料 简要说明 不同于AXI和AXI-lite协议,AXI-Stream协议不是基于地址的数据 ...
最新文章
- EXP6 信息搜集与漏洞扫描
- Java常用的集合类
- Python网络_TCP/IP简介
- 3_6 CommandMode 命令模式
- Python 简单入门学习笔记
- java移动端接口测试_走进Java接口测试之测试框架TestNG数据驱动(入门篇)
- ExtJS+DWR+Spring+Hibernate开发HRMS(3)
- VC下使用Proc连接Oracle数据库
- Thinkphp5之ajax分页实现_paginate()参数详细
- MR Mapper类
- nis+nfs+autofs
- 机器学习中性能评估指标中的准确率(Accuracy)、召回率(Recall=TPR)、精确率(Precision)、误报率(FPR)、漏报率(FNR)及其关系
- 求一元硬币兑换成1分、2分、5分的硬币的兑换方案
- 2022年北京首店补贴及首店首发项目征集申报情况介绍,补贴200万
- UCASE() 函数
- 实战篇-通用的页面列表导出Excel控件
- docker入门_Docker入门
- 没有同理心_在设计上超越同理心
- 小黑算法成长日记8:最优多边形环状运算(加法和乘法)
- 基于html的网页设计-音乐主题
热门文章
- mre下的控件实现(一、公共的宏定义)
- 我所遇到最牛的sshd远程连接
- 5款热门可视化工具对比,老板直呼酷炫!
- 你们期待已久的微信功能,终于要来了
- springboot+mybatis plus+code generate+mysql + swagger ui简单demo
- 2022哪款充电宝质量好?2022充电宝排行
- unity超级马里奥2d游戏开发-03瓦片地图场景搭建
- 云计算机下erp的发展,云计算下企业ERP信息管理系统的应用
- 解决** (ArgumentError) argument error (stdlib) io_lib.erl:187: :io_lib.format(‘ * effective user\‘
- CTF_StegSolve使用方法