网络篇02 | 应用层 kcp(王者荣耀)
kcp是一个快速可靠协议(也可以叫udp的可靠性传输)。结合了tcp的可靠性和udp的传输速度等优点,能以⽐ TCP浪费10%-20%带宽的代价,换取平均延迟降低 30%-40%,且 最⼤延迟降低三倍的传输效果。使用纯算法实现,不负责底层协议(如udp)的收发,内部没有系统调用。
01 kcp报文格式
https://gitee.com/tipsdark/kcp-netty.git
- conv :连接号。UDP是⽆连接的,conv⽤于表示来⾃于哪个客户端。对连接的⼀种替代, 因为有 conv , 所以KCP也是⽀持多路复⽤的。
- cmd :命令类型,只有四种。
IKCP_CMD_ACK,确认命令;
IKCP_CMD_PUSH,数据推送命令;
KCP_CMD_WASK,接收窗⼝⼤⼩询问命令;
IKCP_CMD_WINS,接收窗⼝⼤⼩告知命令。 - frg :分⽚,⽤户数据可能会被分成多个KCP包,发送出去。
- wnd :接收窗⼝⼤⼩,发送⽅的发送窗⼝不能超过接收⽅给出的数值, (其实是接收窗⼝的剩余⼤⼩,这个⼤⼩是动态变化的)
- ts : 时间序列
- sn : 序列号
- una :下⼀个可接收的序列号(连续的序列)。其实就是确认号,收到sn=10的包,una为11
- len :数据⻓度(DATA的⻓度)
- data :⽤户数据
02 名词说明
用户数据:应用层发送的数据,如一张图片2Kb的数据
- MTU:最大传输单元。即每次发送的最大数据
- RTO:Retransmission TimeOut,重传超时时间。
- RTT: 一个报文段发送出去,到收到对应确认包的时间差。
- cwnd:congestion window,拥塞窗口,表示发送方可发送多少个KCP数据包。与接收方窗口有关,与网络状况(拥塞控制)有关,与发送窗口大小有关。
- rwnd:receiver window,接收方窗口大小,表示接收方还可接收多少个KCP数据包
- snd_buf:发送消息的缓存,存放处于发送状态的数据(比如调用了sendto后)
- snd_queue:待发送KCP数据包队列,暂存分片、排序后的数据,当发送缓存有空之后就会放入发送缓存。
- snd_nxt:下一个即将发送的kcp数据包序列号
- snd_una:下一个待确认的序列号
- rcv_buf:接收消息的缓存, 还不能直接供用户读取的数据
- rcv_queue:接收消息的队列, 是已经确认可以供用户读取的数据(已经排好序)
03 技术特点
超时重传时间
- TCP超时计算是RTOx2,这样连续丢三次包就变成RTOx8了,⼗分恐怖;
- ⽽KCP启动快速模式后不x2, 只是x1.5(实验证明1.5这个值相对⽐较好),提⾼了传输速度。
RTO(Retransmission-TimeOut)即重传超时时间,TCP是基于ARQ协议实现的可靠性,KCP也是基于ARQ协议实现的可靠性,但TCP的超时计算是RTO2,而KCP的超时计算是RTO1.5,也就是说假如连续丢同一个包3次,TCP第3次重传是RTO8,而KCP则是RTO3.375,意味着可以更快地重新传输数据。通过4字节ts计算RTT(Round-Trip-Time)即往返时延,再通过RTT计算RTO,ts(timestamp)即当前segment发送时的时间戳。
超时重传机制
- TCP丢包时会全部重传从丢的那个包开始以后的数据;
- KCP是选择性重传,只重传真正丢失的数据包。
快速重传机制
发送端发送了1,2,3,4,5⼏个包,如果2包丢失;
- kcp快速重传机制为:收到远端回复的ACK: 1, 3, 4, 5,当收到ACK3时,KCP知道2被跳过1 次,收到ACK4时,知道2被跳过了2次,此时可以认为2号丢失,不⽤等超时,直接重传2号包,⼤⼤改善 了丢包时的传输速度。
ACK机制对比
- TCP为了充分利⽤带宽,延迟发送ACK(NODELAY都没⽤),这样超时计算会算出较⼤ RTT时间,延⻓ 了丢包时的判断过程。
- KCP的ACK是否延迟发送可以调节。
收包确认机制对比
- ARQ模型响应有两种,UNA(此编号前所有包已收到,如TCP)和ACK(该编号包已收到),光⽤UNA将 导致全部重传,光⽤ACK则丢失成本太⾼,以往协议都是⼆选其⼀,⽽ KCP协议中,除去单独的 ACK包 外,所有包都有UNA信息。
非退让流控
- KCP正常模式同TCP⼀样使⽤公平退让法则,即发送窗⼝⼤⼩由:发送缓存⼤⼩、接收端剩余接收缓存大小、丢包退让及慢启动这四要素决定。但传送及时性要求很⾼的⼩数据时,可选择通过配置跳过后两步,仅⽤前两项来控制发送频率。以牺牲部分公平性及带宽利⽤率之代价,换取了开着BT都能流畅传输的效果。
04 kcp参数
待补充。
05 kcp配置模式
1)工作模式:int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc)
nodelay :是否启用 nodelay模式,0不启用;1启用。
interval :协议内部工作的 interval,单位毫秒,比如 10ms或者 20ms
resend :快速重传模式,默认0关闭,可以设置2(2次ACK跨越将会直接重传)
nc :是否关闭流控,默认是0代表不关闭,1代表关闭
普通模式: ikcp_nodelay(kcp, 0, 40, 0, 0)
极速模式: ikcp_nodelay(kcp, 1, 10, 2, 1)
2)最大窗口:int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);
该调用将会设置协议的最大发送窗口和最大接收窗口大小,默认为32,单位为包。
3)最大传输单元:int ikcp_setmtu(ikcpcb *kcp, int mtu);
kcp协议并不负责探测MTU (最大传输单元),默认 mtu 是 1400 字节
3)最小RTO:
不管是 TCP还是 KCP计算 RTO时都有最小 RTO的限制,即便计算出来RTO为40ms,由于默认的 RTO是100ms,协议只有在100ms后才能检测到丢包,快速模式下为30ms,可以手动更改该值: kcp->rx_minrto = 10;