UDP可靠传输的实现(KCP)
作者:互联网
1. 如何做可靠性传输?
ack机制、重传机制、序号机制、重排机制(网络包乱序)、窗口机制
2. 如何选择TCP和UDP?
当实时性要求高,选择UDP:直播、音视频通话、游戏......
使用短连接时:
- 物联网传感器上报数据,采集完就休眠,采用UDP可以省电
- 大规模服务器心跳包设计,集中服务器监测,用来监控十万台服务器
3. UDP如何可靠?KCP协议的优势是什么?
可靠性UDP为了解决延迟问题 流量 = 带宽,流速 = 延迟 可靠+兼容速率——加序号
- RTO不翻倍:TCP的超时重传间隔RTO是以2倍增加,但KCP以1.5倍增加(可以控制RTO,降低延迟)
- 选择性重传:TCP从丢的那个包开始后面的所有包全部重传,KCP只重传真正丢的包
- 快速重传:KCP使用快速重传机制,可以不考虑RTO,直接重传,改善了丢包时的传输速度
- 非延迟ACK:TCP为了充分利用带宽,延迟发送ACK,计算出较大的RTT,延长丢包的判断过程;KCP的ACK是否延迟可以调节
- ACK+UNA:ARQ模型响应有两种,UNA(全部重传)ACK(丢失成本高),以往协议二选一,KCP中除了单独ACK包外全部采用UNA
- 非退让流控:TCP的慢启动让每个客户端一开始尽量少占一些带宽,KCP采用非退让流控,可以有效利用带宽
KCP以10%-20%的带宽浪费,换取了比TCP快30%-40%的传输速度
4. KCP(https://github.com/skywind3000/kcp)
在学习KCP之前,首先需要有TCP基础,在了解TCP的基础上学习KCP才会比较容易理解
消息流程:发送队列->发送缓存->sendto->recvfrom->接收缓存->接收队列
- 创建ikcpcb【ikcp_create】
- 状态机在loop中固定时长运作一次【ikcp_update】
- 对于recvfrom收到的数据,先放进kcp引擎【ikcp_input】进行处理,再通过【ikcp_recv】读出真正的用户数据(如果ikcp_recv返回0,说明收到了完整数据)
- 对于需要sento的数据,设置【kcp->output】回调函数,规定发送规则(udp/tcp/...),然后把要发送的数据通过【ickp_send】,然后调用【ikcp_update】触发引擎调用output让下层udp发送数据(kcp会分片:一般576byte是支持的(包括ip、udp头部字节),如腾讯游戏的MTU设为500。发送数据前,检测数据被划分成多少个分片,如果超过接收窗口大小则中止发送)
配置模式
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(sendto的最大值)是1400字节(24+1376=1400)
4. 最小RTO:不管是 TCP还是 KCP计算 RTO时都有最小 RTO的限制,即便计算出来RTO为
40ms,由于默认的 RTO是100ms,协议只有在100ms后才能检测到丢包,快速模式下为
30ms,可以手动更改该值: kcp->rx_minrto = 10;
kcp协议头
conv:连接号。 UDP是无连接的, conv用于表示来自于哪个客户端。对连接的一种替代
cmd:命令字。如, IKCP_CMD_ACK确认命令,
IKCP_CMD_WASK接收窗口大小询问命令,
IKCP_CMD_WINS接收窗口大小告知命令,
frg:分片,用户数据可能会被分成多个KCP包,发送出去
wnd:接收窗口大小,发送方的发送窗口不能超过接收方给出的数值
ts:时间戳,在ack包的时候会同时发送ts
sn:序列号
una:下一个可接收的序列号。其实就是确认号,收到sn=10的包, una为11
len:数据长度
data:用户数据
数据发送
缓存逻辑:snd_queue——snd_buf——udp——rcv_buf——rcv_queue
检测对方窗口rmt_wnd是否为0,如果是0要发送探测包,发送数据会根据对方的接收窗口能力去发送,send_buf size不能超过对方窗口大小
snd_buf是用于应答的缓存,只有被应答的包才能从snd_buf中删除,比如:
una = 11 则会把9和10从snd_buf中删除
una = 12 且 ack 9 10 11 13 则会把9 10 11 13均删去
数据接收
rcv_buf:用于对数据包排序,把收到的数据按照sn顺序插入链表
rcv_queue:用于存放已经排好序的连续数据分片,严格递增的数据包会放入rcv_queue
不断调用ikcp_send,数据累积在snd_queue
如果对方没有接收,数据累积在rcv_rcv_buf中
窗口:snd_queue和rcv_queue的大小(如果一直发送很长的数据,最终会攒到snd_buf中)
ikcp_input:recvfrom后,调用它来进行数据处理
ikcp_update->ikcp_flush
ACK确认包、探测远端窗口、发送send_buf数据分片、更新拥塞窗口
标签:UDP,重传,int,ikcp,传输,KCP,buf,kcp 来源: https://blog.csdn.net/Xinyue_Lu/article/details/109229470