KCP传输协议,快速可靠的UDP,和TCP比有什么优势?
本篇博客的视频教程首发于 Youtube:科技小飞哥,加入 电报粉丝群 获得最新视频更新和问题解答。
背景
可能你从来没有听说过KCP是什么,但是你肯定知道TCP和UDP。
这里我们通过KCP来剖析TCP和UDP的区别是什么,并理解为什么有了TCP还需要KCP,以及KCP/UDP具体的使用场景。
KCP是什么
我们都知道:
TCP保证数据准确交付,UDP保证数据快速到达,KCP则是两种协议的一个折中,在力求在保证可靠性的情况下提高传输速度
。
KCP的设计目标是为了解决在网络拥堵的情况下TCP传输速度慢
的问题。
KCP是一个
快速可靠协议
,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。纯算法实现,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据的发送方式,以 callback的方式提供给 KCP。连时钟都需要外部传递进来,内部不会有任何一次系统调用。
TCP是为流量设计的(每秒内可以传输多少KB的数据),讲究的是充分利用带宽。而 KCP是为流速设计的(单个数据从一端发送到一端需要多少时间),以10%-20%带宽浪费的代价换取了比 TCP快30%-40%的传输速度。
KCP没有规定下层传输协议,它只是一个纯算法的实现,但通常使用UDP作为底层协议。所以一般来说可以说KCP是快速可靠的UDP。
我们都知道,TCP是可靠的传输协议。那么KCP的特点就是:快速可靠。
那你可能又有疑问,那么TCP为什么不设计成快速可靠的。看完这篇文章,你的疑惑全部都能解开。
有兴趣可以看KCP的源代码:https://github.com/skywind3000/kcp。
原作者写的README.md
关于KCP的技术特性和使用案例解释的非常详细。同时KCP只有两个文件ikcp.h
, ikcp.c
, 总代码量只有一千多行,非常适合深入学习。更能加深自己对传输层协议的理解。
传输协议
在TCP/IP五层模型协议中,传输协议处在第四层,提供端到端的接口,所以不论是TCP还是UDP,其数据段中都包含了端口号。
在网络中,我们认为传输是不可靠的,而在很多场景下我们需要的是可靠的数据,所谓的可靠,指的是数据能够正常收到,且能够顺序收到,于是就有了ARQ协议,TCP之所以可靠就是基于此。
TCP保证可靠传输的手段:校验和
,流量控制(滑动窗口协议)
,拥塞控制
,ARQ协议
,超时重传
。
ARQ协议
ARQ协议,即自动重传请求(Automatic Repeat-reQuest)
,是传输层的错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。
如果发送方在发送后一段时间之内没有收到确认帧,它通常会重新发送。ARQ包括停止等待ARQ协议
和连续ARQ协议
。
ARQ协议拥有错误检测
(Error Detection)、正面确认
(Positive Acknowledgment)、超时重传
(Retransmission after Timeout)和 负面确认及重传
(Negative Acknowledgment and Retransmission)等机制。
ARQ协议有两种模式:
1.停等ARQ协议
停等ARQ协议相当于发送窗口和接收窗口大小均为1的滑动窗口协议。即发送方发送一个帧后,必须接收到一个确认帧(ACK)才能发送下一个。
很显然。这种等停ARQ协议其信道的利用率很低。
2.连续ARQ协议
可以连续发送多个分组,而不必每发完一个分组就停下来等待对方确认。
有了连续ARQ,现在已经能够在不可靠的网络中传输可靠的数据,但这不意味着可以随意发送数据,带宽是有限的,接收方的负载也是有限的,所以引入了窗口协议,做流量控制。
窗口协议
窗口协议中有两种:
- 拥塞窗口
防止过多的数据注入到网络中,这样可以使网络中的路由器 和链路不至于过载。与拥塞控制相关的有慢启动
、退半避让
、快重传
、快恢复
等。慢启动是在刚开始发送数据时让窗口缓慢扩张,退半避让是在网络拥堵时窗口大小减半,快重传是在网络恢复时及时给予响应,与之配合的就是快恢复。
- 滑动窗口
TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接受端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小的滑动窗口协议。
KCP协议的特征
KCP协议是一个纯粹的ARQ协议,通过重传机制实现UDP数据包的可靠传输。
在谈KCP协议的特征前,还是先贴一下KCP协议的数据段格式。
协议头
可以看到有这么几个字段:
- conv: 连接标识。
- cmd: Command。
- frg: 分片数量,表示随后还有多少个报文属于同一个包。
- wnd: 发送方剩余接收窗口的大小。
- ts: 时间戳。
- sn: 报文编号。
- una: 发送方的接收缓冲区中最小还未收到的报文段的编号。也就是说, 编号比它小的报文段都已全部接收。
- len: 数据段长度。
- data: 数据段. 只有数据报文会有这个字段。
技术特性
TCP是为流量设计的(每秒内可以传输多少KB的数据),讲究的是充分利用带宽。而 KCP是为流速设计的(单个数据包从一端发送到一端需要多少时间),以10%-20%带宽浪费的代价换取了比 TCP快30%-40%的传输速度。TCP信道是一条流速很慢,但每秒流量很大的大运河,而KCP是水流湍急的小激流。KCP有正常模式和快速模式两种,通过以下策略达到提高流速的结果:
TCP vs KCP
-
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(该编号包已收到),光用UNA将导致全部重传,光用ACK则丢失成本太高,以往协议都是二选其一,而 KCP协议中,除去单独的 ACK包外,所有包都有UNA信息。 -
非退让流控:
KCP正常模式同TCP一样使用公平退让法则,即发送窗口大小由:发送缓存大小、接收端剩余接收缓存大小、丢包退让及慢启动这四要素决定。但传送及时性要求很高的小数据时,可选择通过配置跳过后两步,仅用前两项来控制发送频率。以牺牲部分公平性及带宽利用率之代价,换取了开着BT都能流畅传输的效果。
可以看出,TCP是一个公平的网络协议,它保证使用TCP的各方能合理的利用带宽,避免造成网络拥塞。
而KCP是一个自私的网络协议,它首先保证自己的快速到达。而不考虑网络中的其他数据的拥塞情况。是一种通过增加宽带来换取低延迟的协议。
使用场景
看到以上的对比,相比你已经清楚了KCP和TCP的的区别。
那么疑问是不是来了,既然KCP那么好,那么TCP为什么不这么设计呢?KCP有哪些使用场景呢。
KCP使用的场景一般是对延迟要求更高的场景。
- 视频播放/直播 如果使用TCP,一旦发生丢包,TCP会讲后续的包缓存起来,等前面的包重传后再继续发送,延迟会越来越大。基于UDP上做一些重传校验是最好的选择。网易BOBO使用KCP加速主播推流。
- 代理软件 比如v2ray,著名代理软件,Shadowsocks的代替者,集成了KCP协议,使用UDP传输。 还有kcptun等。
- 加速器 比如商业软件
网易CC
和云帆加速
,使用KCP加速文件传输和视频推流。 - 网络游戏 尤其是MOBA游戏和FPS游戏,这类游戏对延迟的要求极高。30ms以内为最好效果。超过100ms基本就无法正常玩游戏。TCP的超时重传是灾难。
为什么Moba游戏中不使用TCP呢?
我们知道,不同类型的游戏因为玩法、竞技程度不一样,采用的同步算法不一样,对网络延迟的要求也不一样。
MOBA/FPS作为竞技类的游戏,游戏中实时高精度同步,或者说延迟容错率的要求还算是比较高的一种。
通过上面对KCP和TCP的比较我们知道,tcp追求的是完全可靠性和顺序性,丢包后会持续重传直至该包被确认,否则后续包也不会被上层接收,且重传采用指数避让策略,决定重传时间间隔的RTO(retransmission timeout)不可控制,linux内核实现中最低值为200ms,这样的机制导致丢包率短暂升高的情况下应用层消息响应延迟急剧提高,并不适合实时性高、网络环境复杂的游戏。
基于udp定制传输层协议,引入顺序性和适当程度或者可调节程度的可靠性,修改流控算法。适当放弃重传,如:设置最大重传次数,即使重传失败,也不需要重新建立连接。kcp的作者是国内优秀开发者,社区也发展良好,KCP也是是目前比较火的UDP快速可靠的解决方案。
哪些游戏使用了UDP
基本上所有的Moba游戏和FPS游戏都采用了UDP+应用层可靠性保证来实现的。
我了解的有这些(当然还有很多其他的游戏):
- 《王者荣耀》 王者荣耀的PVP通信协议确实是基于udp封装的。
- 《和平精英》 现在非常火的吃鸡手游,也是基于udp的。
- 《乱斗西游》
《乱斗西游》的技术发展过程我比较熟悉一点,因为之前我就在网易,同时使用了和乱斗西游同样的服务端游戏引擎:MobileServer
。乱斗西游是国内最早一批的Moba游戏,公测主打从PVE,后来加入PVP玩法,由于是PVE起家,最开始使用和PVE同样的主客机方式,在主客机基础上加了一个中转服务器。左边为主机,右边为客机,通过服务器转发请求。所以当时《乱斗西游》的比赛胜负,主要靠抽签,谁抽到左边谁就赢了,因为右边的延迟太高。
后来乱斗西游更换了UDP协议,在应用层做了尽量的
可靠性保证,允许一定的丢包的情况下保证数据尽快的到达。解决了网络延迟的问题。
其他的可靠UDP解决方案
实际上,除了KCP,市面上还有很多出名的基于UDP实现的可靠协议,都有各自领域很高的实用场景。
其他解决方案也包括:
- QUIC:(Quick UDP Internet Connections),google出品,HTTP3就是运行在QUIC之上,现主要使用在Google Chrome,Google Search,Youtube上。整体性能提升30%以上。
- RakNet: 像CS,Quake等FPS游戏都是采用的这个。
- Enet:可靠的UDP网络库,在UDP上实现了ARQ协议。
- UDT:UDP-based Data Transfer Protocol, 是一种互联网数据传输协议。主要目的是支持告诉广域网上的海量数据传输。
后面我会专门用一整篇文章介绍QUIC协议。
总结
我相信关于KCP的基础概念我已经说的很清楚了,希望这篇文章让你学到了不一样的知识。
那么什么时候需要使用TCP,什么时候使用UDP呢?
答案很简单:
- 当有疑问时,就使用TCP。(when in doubt, use tcp)
- 只有你非常确定你要干什么的时候,才需要使用UDP以及基于UDP封装的协议。
但是无论你是否使用,学习KCP一定能让你对网络协议有更深入的理解,赶快学习吧。
<全文完>