KCP

Note

[toc]

1. TCP协议

传输控制协议(TCP,Transmission Control Protocol)是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议。是传输层的协议。

TCP是为流量设计的(每秒内可以传输多少KB的数据),讲究的是充分利用带宽。而 KCP是为流速设计的(单个数据包从一端发送到一端需要多少时间),以10%-20%带宽浪费的代价换取了比 TCP快30%-40%的传输速度。TCP信道是一条流速很慢,但每秒流量很大的大运河,而KCP是水流湍急的小激流。

超时重传机制

TCP协议要求:发送端每发送一个报文段,就会启动一个定时器等待确认接收信息。接收端收到报文段后就要返回确认信息ACK。在定时器超时前数据没被确认,TCP就认为“丢包”。接着会重传这个包,直到接收端确认为止。这也是TCP可靠性的来源。

RTO RTT

重传超时时间(RTO,Retransmission TimeOut),TCP使用自适应算法动态调整RTO的值。
往返时延 (RTT,Round Trip Time)也就是数据包从发出去到收到对应 ACK 的时间。RTT 是针对连接的,每一个连接都有各自独立的 RTT。

TCP中对于RTO、RTT的控制算法有很多种,但都比较极端,比如Karn/Partidge Algorithm的算法采用是若超时则直接将RTO翻倍。如果还没收到就再 * 2... 也有如Jacobson/ Karels算法,是一种加权平均的经验算法。但其中的参数确实有点神秘魔法含义,反正能用就行。

快速重传机制

发送端收到来自接收端的三个相同的ACK时就会触发快速重传,(其实就算报文没丢失,也有可能因为报文到达的顺序不一致导致ACK的重复发送,而报文丢失必定会出现3个ACK,分别来自N-1和后边的两个报文)。

普通的超时重传太过笨重。丢失一个报文后就会重发之后的所有报文(为了确保可靠性而过分的慎重)。但快速重传机制下,发送端收到某个报文的三次ACK后就会立即重传,而接收端会将之前收到的报文先缓存起来,然后在收到丢失的报文后,继续正常工作。

延迟 ACK

接收端收到数据时要发送一个ACK(期待的下一个报文编号)表示确认收到。但只发送确认对带宽有所浪费,所以会延迟等待看是否有响应数据需要发送,若有则附着其中发送回去。一般等待最多200ms左右。这里的200ms指的是一个内部的计时器,每隔200ms检查有没有ACK要发送。例如有一个数据段在185ms到达,那么它最迟在200ms发送ACK,而不是等待到385ms看没有响应数据再发送。(这对网络延迟造成了更严重的影响)

ACK,UNA

ARQ响应模型有两种,即UNA(当前未收到确认信息的包编号,或理解成:此编号前所有包已收到,如TCP)和ACK(该编号包已收到),光用UNA将导致全部重传,光用ACK则丢失成本太高,二者取长补短会更好。

滑动窗口

一般来说,发送端的数据可以分为四类

  • Sent and Acknowledged:数据已发送,且收到来自接收端的ACK。
  • Sent , But Not yet Acknowledged:数据已发送,但尚未收到来自接收端的ACK。
  • Not Sent, Recipient Ready to Receive:数据未发送,且接收端已经准备好接收
  • Not Sent, Recipient Not Ready to Recieve: 数据未发送,且接收端也尚未准备好接收。
image

而所谓的滑动窗口,就是指的一个动态的 发送/接收 队列。如上图,发送端的滑动窗口可以分为两部分,即发送窗口(已经发送但未收到ACK)和可用窗口(接收端还能接收但发送端尚未发送)。

而接收端的滑动窗口也是类似的,其响应的WIN值所表示的就是窗口大小(剩余可以接收的大小),这样发送端在收到后可以动态调整发送的数据大小,甚至是暂停发送(让接收端先把之前的数据消化掉),以此达到流量控制,减少堵塞的目的。

2. KCP协议

KCP是一个纯粹的ARQ协议,通过重传机制实现UDP数据包的可靠传输,是一个在UDP之上SESSION之下的协议。以比 TCP 浪费 10%-20% 的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。是纯算法实现,所以要自己定义下层数据包的收发方式。

KCP 提高流速

  • KCP启动快速模式后RTO超时x1.5(实验证明1.5这个值相对比较好),提高了传输速度。相较于TCP,若三次超时则变成RTO * 8。

  • KCP是选择性重传,只重传真正丢失的数据包。KCP也有基本的超时重传机制与快速重传机制。KCP会直接重传失序次数过多的报文,而非等待其超时。

假设发送方依次发送了 1, 2, 3, 4 号报文, 随后收到 1, 3, 4 号 ACK. 收到 3 号 ACK 时, 我们知道 2 号 ACK 失序了一次, 收到 4 号 ACK 时, 我们知道 2 号失序了两次. ACK 失序次数越多说明它丢包的概率越大。

  • KCP的ACK是否延迟发送是可以调节的。KCP中除了单独的ACK包之外,其他所有包都包含有UNA信息。

KCP 报文结构

KCP中共有四种报文,分别是

  • 数据报文 IKCP_CMD_PUSH
  • 确认报文 IKCP_CMD_ACK
  • 窗口探测报文 IKCP_CMD_WASK
  • 窗口通知报文 IKCP_CMD_WINS

报文结构如下:

0               4   5   6       8 (BYTE)
+---------------+---+---+-------+
|     conv      |cmd|frg|  wnd  |
+---------------+---+---+-------+   8
|     ts        |     sn        |
+---------------+---------------+  16
|     una       |     len       |
+---------------+---------------+  24
|                               |
|        DATA (optional)        |
|                               |
+-------------------------------+
  • cmd:域用来区分是具体哪种报文。
  • conv:用来表示连接标识。
  • frg:其后报文数量。
  • wnd:剩余接收窗口大小。
  • ts:时间戳。
  • sn:报文编号
  • una:尚未收到ACK的报文编号(小于una的都接收了)
  • len:数据长度
  • data:数据部分

利用ts计算出RTT(往返时间)从而判断是否需要超时重传。同时还有一个MSS(最大报文段大小)用来限制数据包的大小,超过了就要切片成多个报文。因此引入frg表示报文数量。

KCP 队列与缓冲区

注意到struct IKCPCB里有四个struct IQUEUEHEAD,是表示队列,也即发送队列,接收队列,发送缓冲区,接收缓冲区,都是手动实现的双向循环链表。且用大量的宏定义实现了队列的相关操作。这些队列有一个初始的头结点Head(不存数据),随后可在头尾进行插入删除等操作。

https://luyuhuang.tech/2020/12/09/kcp.html

KCP 发送 接收 重传

KCP 的整个发送, 接收与重传的流程大体如下:

  • 调用 ikcp_send 发送数据, 创建报文段实例, 加入 snd_queue 中.
  • ikcp_update 会在合适的时刻调用 ikcp_flush.
    • ikcp_flush 会做:
    • 发送 ACK 列表中所有的 ACK;
    • 检查是否需要发送窗口探测和通知报文, 如果需要就发送相应的报文;
    • 根据发送窗口大小, 将适量的报文段从 snd_queue 移入到 snd_buf 中;
    • 将 snd_buf 中满足条件的报文段都发送出去. 这里的条件有:
    • 新加入 snd_buf 中, 从未发送过的报文直接发送出去;
    • 发送过的, 但是在 RTO 内未收到 ACK 的报文, 需要重传;
    • 发送过的, 但是 ACK 失序若干次的报文, 需要执行快速重传.
    • 根据丢包情况计算 ssthresh 和 cwnd.
    • 这样, 刚才调用 ikcp_send 传入的数据就在 ikcp_flush 中被发送出去了.
  • 报文到达对端.
  • ikcp_input 会被调用, 解析收到的数据:
    • 所有的报文都有 una 字段, 根据 una 将相应的报文标记为已送达;
    • 如果是 ACK 报文, 就将相应的报文标记为已送达;
    • 如果是数据报文, 就将它放入 rcv_buf, 然后将 rcv_buf 中顺序正确的报文移入 rcv_queue; 接着将相关信息插入 ACK 列表, 在稍后的 ikcp_flush 调用中会发送相应的 ACK;
    • 如果是窗口探测报文, 就标记 "需要发送窗口通知". 在稍后的 ikcp_flush 调用中会发送窗口通知报文;
    • 包括窗口通知报文在内的所有报文都有 wnd 字段, 据此更新 rmt_wnd;
    • 根据 ACK 失序情况决定快速重传;
    • 计算 cwnd.
  • 调用 ikcp_recv 接收数据, 从 rcv_queue 中读取数据.

KCP 拥塞控制

与TCP相同,KCP也有拥塞窗口、慢启动、快速恢复等拥塞控制措施。

开始时cwnd=1,(congestion window,拥塞窗口,即发送端的一个可滑动窗口)。每经过一个RTT时间后就翻倍,但也不会无限指数增长。超过阈值后就是线性增长,这个阶段叫拥塞避免,这个阈值叫做慢启动阈值(ssthresh,Slow Start Threshold)。整个过程就叫慢启动。

随着cwnd的增大,网络最终被填满,就会出现丢包。此时说明cwnd应该减小,即丢包退让。KCP对此的处理是:如果发生超时重传, 就进入慢启动(将ssthresh置为cwnd的一半,然后cwnd重新从1开始慢启动); 如果发生快速重传, 就进入快速恢复(将ssthresh置为cwnd的一半,cwnd设置比ssthresh略微大一点,再以拥塞避免的方式线性增长).

[图片上传失败...(image-4153eb-1628154473200)]

KCP 源码

https://github.com/skywind3000/kcp

KCP的代码是比较精炼的,只有ikcp.cikcp.h 两个文件。

其中 ikcp.h 提供了结构体定义,队列操作,接口函数声明等。ikcp.c 是对各函数方法的具体实现。

  • ikcp_create 创建一个KCP的实例,将其中的各项成员初始化后返回该实例的指针。其中IUINT32 conv 参数用于标识该KCP连接。通信双方必须协商相同的conv才可以交流。该连接发出的每个报文段都会带上conv,同理也只接受包含conv的报文段。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容

  • KCP介绍 1 简介 KCP是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 3...
    hexg1016阅读 19,016评论 2 4
  • 第5章 运输层 5.1 运输层协议概述 5.1.1 进程之间的通信 从通信和信息处理的角度看,运输层向它上面的应用...
    辘轳鹿鹿阅读 883评论 0 1
  • 一、基本慨念:IP地址 =网络号+主机号.域名系统:是一个分布数据库,它提供将主机名(就是网址啦)转换成IP地址的...
    JoeJiang阅读 473评论 2 8
  • 套接字选项SO_RESUEADDR 即使端口处于2MSL状态,使用该选项,仍然能够在该端口建立连接。服务器常会设置...
    Myth52125阅读 1,403评论 0 0
  • Catalog HTTP 协议1.1 特性1.2 HTTP 报文1.3 会话1.4 缓存1.5 缺点1.6 HTT...
    是ADI呀阅读 534评论 0 1