zl程序教程

您现在的位置是:首页 >  工具

当前栏目

KCP源码demo

源码 Demo
2023-09-14 09:05:28 时间

前言

KCP作为一个比较轻量级的UDP可靠数据传输协议,在参照TCP拥塞控制的基础上实现了ARQ的功能。走读源码了解启动的原理,对于UDP可靠传输设计具有借鉴意义。

1 整体数据流

KCP整体数据流程由四个缓冲队列来完成,数据在缓冲队列进行迁移:其中队列都是排序的,其中接收队列接收的数据必须是联系的,也就是可靠的。

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

2 接收流程

2.1用户层从KCP接收数据:

用户最终读取一帧数据。

int ikcp_recv(ikcpcb *kcp, char *buffer, int len);
{
	//1. 计算一帧数据大小:多个分片包组装,当seg->frg == 0表示包最后一个分片
	int ikcp_peeksize(const ikcpcb *kcp)
	//2. 拷贝完整一帧数据到buffer中
	//3. 检查rcv_buf是否有连续数据可以拷贝到rcv_queue队列、并更新接收序号rcv_nxt
	//4. 接收窗口从满(>=rcv_wnd),到有数据可写,需要通知发送端,利用probe |= IKCP_ASK_TELL设置标志位。
}

2.2 底层接收数据到KCP:

recvfrom从网络读取数据加入到KCP组件中。

int ikcp_input(ikcpcb *kcp, const char *data, long size);
{
	//1. 获取send_buf,中小于una的序列号,并将数据从发送缓冲区移除。
	static void ikcp_parse_una(ikcpcb *kcp, IUINT32 una)

	//2. 更新下一个需要对方应答的snd_una,序号
	static void ikcp_shrink_buf(ikcpcb *kcp)

	//3. 四种cmd处理
	{
		//3.1 应答包处理:IKCP_CMD_ACK
		//3.1.1 更新kcp->rx_rto:超时重传时间
		ikcp_update_ack(kcp, _itimediff(kcp->current, ts));
		//3.1.2 确认ack,移除send_buf对应的分片
		static void ikcp_parse_ack(ikcpcb *kcp, IUINT32 sn)
		//3.1.3 更新确认下一个需要应答的包snd_una
		static void ikcp_shrink_buf(ikcpcb *kcp)
		
		//3.2 数据包处理:IKCP_CMD_PUSH 
		//3.2.1 只接收接收窗口内数据:[kcp->rcv_nxt + kcp->rcv_wnd]
		//序号和时间戳加入acklist链表,这里有一个扩容的动作
		static void ikcp_ack_push(ikcpcb *kcp, IUINT32 sn, IUINT32 ts)
		//3.2.2 创建分片保存数据
		static IKCPSEG* ikcp_segment_new(ikcpcb *kcp, int size)
		//3.2.3 分片数据经过窗口和查重,按照递增顺序插入队列rcv_buf末尾
		//再次将rcv_buf中符合期望接收序列号rcv_nxt加入rcv_queue队列
		void ikcp_parse_data(ikcpcb *kcp, IKCPSEG *newseg)

		//3.3 窗口探测包处理:IKCP_CMD_WASK
		//设置标记IKCP_ASK_TELL
		kcp->probe |= IKCP_ASK_TELL;  

		//3.4 窗口通知包:IKCP_CMD_WINS
		//不做处理,因为上面解析包已经获取到远端窗口:kcp->rmt_wnd
	}
}

 

2.2.1 更新RTT和RTO等参数,该算法与TCP保持一致:

/*
> 1. 第一次测量,rx_srtt = rtt,rx_rttval = rtt / 2
> 2. 以后每次测量:
    rx_srtt =(1-a) * rx_srtt + a * rtt,a取值1/8
    rx_rttval= (1-b) * rx_rttval + b * |rtt - rx_srtt|,b取值1/4
    rto = rx_srtt + 4 * rx_rttval
    rx_rto = MIN(MAX(rx_minrto, rto), IKCP_RTO_MAX)
*/
static void ikcp_update_ack(ikcpcb *kcp, IINT32 rtt)

3 发送流程

3.1 用户数据加入KCP

用户数据加入到KCP队列中

int ikcp_send(ikcpcb *kcp, const char *buffer, int len)
{
	//1. 开启流传输模式:kcp->stream,需要将最后一个分片填充到:kcp->mss最大分片大小
	//2. 对buffer数据进行分片;
	//3. 将分片数据加入snd_queue队列,分片采用依次递减方式,一帧数据最后一个分片为0。
}

3.2 KCP发送数据 

void ikcp_update(ikcpcb *kcp, IUINT32 current)
{
	//1. 至少10ms的间隔触发一次
	//2. 刷新
	void ikcp_flush(ikcpcb *kcp)
	{
		//1. 计算剩余的窗口
		static int ikcp_wnd_unused(const ikcpcb *kcp)
		//2. 发送ack包
		//2.1 获取确认序号和时间戳
		static void ikcp_ack_get(const ikcpcb *kcp, int p, IUINT32 *sn, IUINT32 *ts)
		//2.2 打包一个分片
		static char *ikcp_encode_seg(char *ptr, const IKCPSEG *seg)
		//2.3 拼接多个ack包,达到kcp->mtu后发送
		static int ikcp_output(ikcpcb *kcp, const void *data, int size)
		{
			//回调到用户函数
			kcp->output
		}

		//3. 窗口为0:kcp->rmt_wnd,7s探测一次,kcp->probe & IKCP_ASK_SEND,发送:IKCP_CMD_WASK包
		//打包:IKCP_CMD_WASK包,拼接多个ack包,达到kcp->mtu后发送
		ptr = ikcp_encode_seg(ptr, &seg);
		//4. 发送数据包:IKCP_CMD_PUSH
		//4.1 将符合要求的数据从snd_queue移动到snd_buf队列
		//4.2 处理重传数据
		//第一次发送:segment->xmit == 0
		//重发时间:current + segment->rto
		//快速重传利用重传次数判断:segment->xmit <= kcp->fastlimit
		//打包:IKCP_CMD_WASK包,拼接多个ack包,达到kcp->mtu后发送
		ikcp_encode_seg
		//如果发生了快速重传,拥塞窗口阈值降低为当前未确认包数量的一半或最小值 
		kcp->ssthresh = inflight / 2;
		//丢包:窗口减半
		kcp->ssthresh = cwnd / 2;
	}
}

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓