为什么要做UDP可靠性传输
如何做到可靠性传输
做到可靠性传输,可以通过如下机制实现
(1)延迟ACK机制
- 发送端将数据发送出去之后,需要等待对方的确认应答
- 如果有确认应答,说明数据已经成功到达对端
- 如果没有收到应答,不能说数据一定丢失了,但是数据丢失的可能性比较大
注意: nagle算法和ack是冲突的,对时延敏感的应用会禁用nagle算法
(2)重传机制
- 数据重传,是发送端发送数据之后如果没有收到对端ACK,那么继续重发数据
- 常用方式是在重发数据之前,等待确认应答来指定超时时间,如果超过了该时间还没有收到确认应答,发送端将进行重传
(3)序号机制
- 序号机制可以保证数据包的顺序
- 序号既可以在数据发送包内,也包含在响应报内
- 接收端:接收端接收到发送包之后,可以根据序号对数据进行重组
- 发送端:在传送一个数据包时,会把该数据包放入重传队列中,同时启动计数器,如果收到对端的确认应答,则通过序号识别,将该数据包从重传队列中删除;如果超时仍未收到对端的确认应答,则重发数据
(4)重排机制
- 重传机制,指的是对数据段进行排序重组
- 可以借助序号机制,使用序号对数据段进行排序
面试题:TCP是如何保证顺序的?
- 网络层(IP层)不保证包的顺序,包的有序性是在TCP层中实现的
- TCP协议通过:序列号+重排机制+ACK机制,实现了包的有序性
- 在SYN报文中,序列号用于交换彼此的初始序号
- 在其他报文中,序列号用于保证包的顺序
(5)窗口机制
- 窗口机制主要用于流量控制和拥塞控制。
下面以TCP为例,具体讨论一下
什么是(滑动)窗口机制
在建立TCP连接的同时,也可以确定发送数据包的单位,称其为“最大消息长度”(MSS)。TCP在发送数据时,是以MSS的大小将数据进行分割发送。
以一个数据段为单位,每发一个数据段就进行一次确认应答处理。这种传输方式的缺点:包的往返时间越长,通信性能就越低。
TCP引入窗口来解决这个问题。如下图:
- 确认应答不再是以每个分段,而是以更大的单位进行确认,发送端主机在发送了一个段之后不必一直等待确认应答,而是继续发送
- 窗口大小就是指无需等待确认应答就可以继续发送数据的最大值。下图窗口大小是3个分段
- 如下图所示,黄色部分表示窗口。
- 这个窗口中的数据即使没有收到应答确认也可以被发送出去。
- 不过,在整个窗口的确认应答没有到达之前,如果其中部分数据出现丢包,那么发送端仍然需要重传。
- 在收到确认应答的情况下,将窗口滑动到确认应答中的序列号的位置。这样可以顺序得将多个段同时发送提高通信性能。
- 这种机制也被称为窗口滑动机制
流量控制:控制发送方发送的速度(窗口大小)
- 流量控制是对发送方和接收端之间的处理速度进行平衡。如果没有这种机制,就可能出现这样一种情况:发送端一直在发送数据,接收端可能由于本身业务原因没能够及时接收数据,一些本该接收的数据被丢弃,从而触发发送端的重发机制,导致网络流量的浪费。
- TCP通过流量控制可以让发送端根据接收端的实际接收能力控制发送数据的数量。接收端主机向发送端主机通知自己可以接收数据的大小,于是发送端会发送不超过这个限度的数据。该大小限度就被称作窗口大小。
- TCP首部中,专门有一个字段用来通知窗口大小。接收主机将自己可以接收的缓冲区大小放入到这个字段中通知给发送端。这个字段的值越大,说明网络的吞吐量越大。当接收端的这个缓冲区一旦面临数据溢出时,窗口大小的值也会随之被设置成一个更小的值发送给发送端,从而控制数据发送量。
问题,如果TCP协议栈中的缓冲区满了会出现什么情况?也就是对端没有及时把内核缓冲区中的数据读走会出现什么情况?
- 客户端发送缓冲区满了send就写不进去了,直接返回-1
- 如果客户端一直发送但是对端不及时读出来,那么对端就会告知客户端不要在发送数据了(滑动窗口关闭)
拥塞控制(慢启动,找到一个合适的网络速度)
有了窗口控制,发送端和接收端就能够连续发送大量数据包。不过,如果在通信刚开始的时候就发送大量数据,也可能引发其他问题。
一般来说,计算机网络都处在一个共享的环境。因此有可能会因为其他主机之间的通信使得网络拥塞,在网络出现拥堵时,如果突然发送一个较大的数据,就可能导致整个网络的瘫痪
解决方法:慢启动,即慢启动,将这个拥塞窗口的大小设置为1个数据段(1MSS)发送数据。之后每收到一次确认应答,拥塞窗口的值就加1。在发送数据包时,将拥塞窗口的大小与接收端主机通知的窗口大小做比较,然后按照它们当中较小的那个值,发送比其还小的数据量。
问题:在使用窗口控制中,如果出现段丢失该怎么办?
① 情况1
- 丢失描述:发送端没有收到接收端的确认应答。
- 解决方案:这种情况,数据已经给到达对端,不需要进行重发。使用了窗口机制,某些确认应答即使丢失也无需重发,可以通过下一个确认应答进行确认。
②情况2
- 丢失描述:发送端会重复的收到接收端的同一序号应答。
- 解决方案:发送端主机如果连续三次接收到同一个确认应答,就会将其对应的数据进行重发。
KCP协议原理
KCP简介
KCP是一个快速可靠传输ARQ(Automatic Repeat-reQuest)协议,相比于TCP,KCP以浪费10%20%的带宽代价,换取比TCP快30%40%的传输速度,并且最大延迟降低三倍的效果。
KCP为纯算法实现,不负责底层协议的收发,需要使用者自己定义下层数据包的发送方式,以callback的方式提供给KCP。KCP内部不会有任何的系统调用,就连时钟都需要外部传递进来。
KCP整个协议只有ikcp.h,ikcp.c两个源文件,使用者可以很方面的集成到自己的协议栈中。比如,你实现了一个P2P或某个基于UDP的协议,而缺乏一套完善的ARQ可靠协议,那么简单的拷贝这两个文件到已有项目中,稍微修改下,即可使用。
KCP特性
相比于TCP,KCP具有如下特性
(1)ROT翻倍 VS 不翻倍
- TCP超时计算是RTOx2,这样连续丢三次包就变成RTOx8了,十分恐怖;
- KCP启动快速模式后不x2,只是x1.5(实验证明1.5这个值相对比较好),提高了传输速度。
(2)选择性重传 VS 全部重传
- TCP丢包时会全部重传从丢的那个包开始以后的数据
- KCP是选择性重传,只重传真正丢失的数据包
(3)快速重传
- 机制:跳过多少个包马上重传(若使用过了快速重传,可以不考虑RTO)。
- 示例:发送端发送了1,2,3,4,5几个包,然后收到远端的ACK: 1, 3, 4, 5,当收到ACK3时, KCP知道2被跳过1次,收到ACK4时,知道2被跳过了2次,此时可以认为2号丢失,不用等超时,直接重传2号包,大大改善了丢包时的传输速度。
(4)延迟ACK VS 非延迟ACK
- TCP为了充分利用带宽,延迟发送ACK( NODELAY都没用),这样超时计算会算出较大 RTT时间,延长了丢包时的判断过程;
- KCP的ACK是否延迟发送可以调节
(5)UNA VS ACK+UNA
- ARQ模型响应有两种, UNA(此编号前所有包已收到,如TCP)和ACK(该编号包已收到),光用UNA将导致全部重传,光用ACK则丢失成本太高,以往协议都是二选其一;
- KCP协议中,除去单独的 ACK包外,所有包都有UNA信息。
(6)非退让流
- KCP正常模式同TCP一样使用公平退让法则,即发送窗口大小由:发送缓存大小、接收端剩余接收缓存大小、丢包退让及慢启动这四要素决定。
- KCP传送及时性要求很高的小数据时,可选择通过配置跳过后两步,仅用前两项来控制发送频率。以牺牲部分公平性及带宽利用率之代价,换取了开着BT都能流畅传输的效果。
KCP一些名词说明
- 用户数据:应用层发送的数据,比如一张图片2KB的数据
- MTU:最大传输单元,即每次发送的最大数据
- RTO,超Retransmission TimeOut,重传超时时间。
- cwnd:ongestion window,拥塞窗口,表示发送方可发送多少个KCP数据包。与接收方窗口有关,与网络状况(拥塞控制)有关,与发送窗口大小有关。
- rwnd:receiver window,接收方窗口大小,表示接收方还可接收多少个KCP数据包。
- snd_queue:待发送的KCP数据包队列
- snd_nxt:下一个即将发送的kcp数据包序列号
- snd_una:下一个待确认的序列号
KCP协议格式
KCP是一个可靠的传输协议,UDP本身是不可靠的,所以需要额外信息来保证传输数据的可靠性。因此,我们需要在传输的数据上增加一个包头。用于确保数据的可靠、有序。
0 4 5 6 8 (BYTE)
- conv:连接号。UDP是无连接的,conv用于表示来自哪个客户端。对连接的一种替代,因为有conv,因此KCP也是支持多路复用
- cmd:命令类型,如下表
- frg:分片,用户数据可能会被分成多个KCP包,发送出去
- wnd:接收窗口大小,发送方的发送窗口不能超过接收方给出的数值, (其实是接收窗口的剩余大小,这个大小是动态变化的)
- ts:时间序列
- sn:序列号
- una:下一个可接收的序列号。其实就是确认号,收到sn=10的包,una为11
- len:数据长度(DATA的长度)
- data:用户数据
KCP发送数据过程
KCP收发数据流程
检测对方接收窗口rmt_wnd是否为0,如果是0则要发送探测包。
发送数据,根据对方的接收窗口能力去发送。
1)snd_buf:解决重传的问题,只有被应答的包才能从buf删除。
2)对方窗口大小,snd_buf不能超过对方窗口大小。
KCP会不停的进行update更新最新情况,数据的实际发送在update时进行。发送过程如下图:
在KCP中,数据被拆分成Segment后:
文章目录
https://github.com/bamboo-blue/KCP_Learn