kcp源码剖析 (3)

kcp使用的是选择重传,也就是要记入某个包跳过的次数,如果跳过大于2次,则重传

	IUINT32 sn;       //编号 确认编号或者报文编号
	IUINT32 una;      //代表编号前面的所有报都收到了的标志
在ikcp_input中调用
ikcp_parse_una(kcp, una);//删除错误的分片:序号小于待确认una的分片 这个是删除多余重复的分片
ikcp_shrink_buf(kcp);//更新snd_una为snd buf中seg->sn或kcp->snd_nxt

ikcp_parse_data源码分析

  1. 丢弃sn > kcp->rcv_nxt + kcp->rcv_wnd的segment; 即丢掉比窗口大的
if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) >= 0 ||
		_itimediff(sn, kcp->rcv_nxt) < 0) {
		ikcp_segment_delete(kcp, newseg);
		return;
	}
  1. 逐一比较rcv_buf中的segment,若重复丢弃,非重复,新建segment加入;

	for (p = kcp->rcv_buf.prev; p != &kcp->rcv_buf; p = prev) {
		IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node);
		prev = p->prev;
		if (seg->sn == sn) {
			repeat = 1;//这里 即sn已经收到了,重复收到
			break;
		}
		if (_itimediff(sn, seg->sn) > 0) {
			break;
		}
	}

	if (repeat == 0) { //如果没有错误 加入队列
		iqueue_init(&newseg->node);
		iqueue_add(&newseg->node, p);
		kcp->nrcv_buf++;
	}	else {
		ikcp_segment_delete(kcp, newseg); //重复删除
	}
  1. 检查rcv_buf的包序号sn,如果是待接收的序号rcv_nxt(seg->sn == kcp->rcv_nxt),且可以接收 (接收队列小 于接收窗口) 不是有序的则留在了buf里,有序的取出到rcv_queue
    转移segment到rcv_buf,nrcv_buf减少,nrcv_que增加,rcvnxt增加;
	// move available data from rcv_buf -> rcv_queue
	while (! iqueue_is_empty(&kcp->rcv_buf)) { //除非buf空或者
	//kcp->rcv_buf.next 循环变量buf 也就是要求续航是 1 2 3这样
	//有可能收到 1 3 2 这样导致错误吗
		IKCPSEG *seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node);
		if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) {
			iqueue_del(&seg->node);
			kcp->nrcv_buf--; //这个是buf长度
			iqueue_add_tail(&seg->node, &kcp->rcv_queue);
			kcp->nrcv_que++; //queue长度
			kcp->rcv_nxt++; //下一个需要等待的编号
		}	else {
			break;
		}
	}

完整代码

void ikcp_parse_data(ikcpcb *kcp, IKCPSEG *newseg)
{

	struct IQUEUEHEAD *p, *prev;
	IUINT32 sn = newseg->sn;
	int repeat = 0;
	
	if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) >= 0 ||
		_itimediff(sn, kcp->rcv_nxt) < 0) {
		ikcp_segment_delete(kcp, newseg);
		return;
	}

	for (p = kcp->rcv_buf.prev; p != &kcp->rcv_buf; p = prev) {
		IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node);
		prev = p->prev;
		if (seg->sn == sn) {
			repeat = 1;
			break;
		}
		if (_itimediff(sn, seg->sn) > 0) {
			break;
		}
	}

	if (repeat == 0) {
		iqueue_init(&newseg->node);
		iqueue_add(&newseg->node, p);
		kcp->nrcv_buf++;
	}	else {
		ikcp_segment_delete(kcp, newseg);
	}

#if 0
	ikcp_qprint("rcvbuf", &kcp->rcv_buf);
	printf("rcv_nxt=%lu\n", kcp->rcv_nxt);
#endif

	// move available data from rcv_buf -> rcv_queue
	while (! iqueue_is_empty(&kcp->rcv_buf)) {
		IKCPSEG *seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node);
		if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) {
			iqueue_del(&seg->node);
			kcp->nrcv_buf--;
			iqueue_add_tail(&seg->node, &kcp->rcv_queue);
			kcp->nrcv_que++;
			kcp->rcv_nxt++;
		}	else {
			break;
		}
	}

#if 0
	ikcp_qprint("queue", &kcp->rcv_queue);
	printf("rcv_nxt=%lu\n", kcp->rcv_nxt);
#endif

#if 0
	printf("snd(buf=%d, queue=%d)\n", kcp->nsnd_buf, kcp->nsnd_que);
	printf("rcv(buf=%d, queue=%d)\n", kcp->nrcv_buf, kcp->nrcv_que);
#endif
}

una之前的都已经有序了, 入una 为7 之前 123456 已经有序,被取走到queue里了,小于una 如果还在buf里都已经在调用ikcp_parse_una被删除了
ikcp_input中的ikcp_shrink_buf修改una
调用 ikcp_parse_una 来确定已经发送的数据包有哪些被对方接收到。KCP 中所有的报文类型均带有 una 信息。发送端发送的数据都会缓存在 snd_buf 中,直到接收到对方确认信息之后才会删除。当接收到 una 信息后,表明 sn 小于 una 的数据包都已经被对方接收到,因此可以直接从 snd_buf 中删除。同时调用 ikcp_shrink_buf 来更新 KCP 控制块的 snd_una 数值。
ikcp_shrink_buf完整代码

static void ikcp_shrink_buf(ikcpcb *kcp)
{
	struct IQUEUEHEAD *p = kcp->snd_buf.next;
	if (p != &kcp->snd_buf) { //kcp->snd_buf.next != kcp->snd_buf 意思是不为空吧
		IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node);
		kcp->snd_una = seg->sn;
	}	else {
		kcp->snd_una = kcp->snd_nxt;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值