C#网络层 Kcp研究

之前在公司分析过kcp源码,并且做了ppt。为了防止过了几年又忘了,在此记录下,方便回来查阅。

kcp就是一个带序号的网络协议,纯算法实现。他与tcp协议有些地方很像,但做了改良。

先说结论,应用层我所理解他比tcp好的地方是。

kcp优点
1.他的协议也和tcp一样是安全的
2.他的很多参数是可配的,比如dead_link,时间片,重传几次条件,滑动窗口大小,数据片大小等
3.他的rto和ssthresh 可以自己搞个觉得更牛逼的
4.在网络比较抖动的情况下,超时重传比tcp表现更好
5.重传机制比Tcp好

kcp缺点 
1.有时候会产生数据抛弃现象,比如滑动窗口不够的时候
2.他比tcp协议的正常情况还多了4个byte
3.用法比tcp多了几个步骤


在继续深入之前,先想一个问题。如何设计一个可靠的网络协议?

这样做理论可行,但实践会有很多问题。比如这一次通信该发送多少数据,如果我这次发送发生意外导致数据没法过去,那就干等吗?这样会造成极大的浪费。

先说一下tcp这块是采用滑动窗口协议,以及拥塞控制来解决意外发生时的问题。

滑动窗口协议(Sliding Window Protocol):
属于TCP协议的一种应用,用于网络数据传输时的流量控制,以避免拥塞的发生。该协议允许发送方在停止并等待确认前发送多个数据分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输,提高网络吞吐量。

拥塞控制:
虽然TCP有了滑动窗口这个大杀器能够高效可靠的发送大量的数据,但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题,因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵,在不清楚当前网络状态下,贸然发送大量的数据是很有可能引起雪上加霜的,造成网络更加堵塞。

换个小白也能听懂的话就是。

滑动窗口协议:比如我有一块缓存1KB,现在用了512B,如果我要发600B的数据,那么实际下次发包的数据为Math.Min(600B,(1KB-512B)) = 512B,那剩下的88B就是剩下的。当发送端接到消息说之前的512B已经确定接受了,会把剩下88B发送出去。这个也是为什么TCP有粘包的问题。

拥塞控制:就是我现在不知道网络情况怎么样,我慢慢通过试探,逐步加大数据的发送量。就好比交通如果大家都堵在一个路口,这时候又来了一大堆车,那么他们也一定会堵着,那不如只来几辆车,损失的只是这几辆车的时间。

具体方式如下:

 用简单的话说,一开始一次发送数据量少,然后指数级增长,如果超过阈值了,他会线性增长。如果网络堵塞,他又直接掉到从0开始同时把阈值对半砍。

再说消息发送超时的问题:简单做法是,可以设置一个时间,当发送这条消息开始计时,如果在发送时间超过这个时间那就认为超时,这时候在发送这条消息。

再说一下TCP的数据结构:

 学过网络通信的都应该见过这个图。这里面的序号和确认号对应的就是上面的Seq和Ack其他数据感兴趣的同学随便百度谷歌就能知道。

数据发送图示:

 可以看出他的Ack = Seq + 这次发送的数据包大小 + 1

Tcp的超时重传:

 可以看出,他并不是收到了哪个包Ack就返回哪个包,而是把中间断的最小的进行返回。这时候就有问题,发送端并不知道接收端真实收包情况。万一这时候 5这块网络发生异常,那么3 4 就有可能触发超时重传机制重发消息。这样产生冗余消息,造成了浪费。

下面是我总结的Tcp的缺点:

1.需要3次握手 4次挥手,过程什么繁琐,建立通信时间长,如果我中途握手或者挥手中间某条消息对端没收到,可能会引发很多需要处理的问题。
2.他的数据都是以字节为单位,那么用户得自己设计个粘包协议才能处理数据。
3.他的丢包产生的重传机制有点不科学,比如 像刚才 2包丢了,3、4、5包收到以后一直发ACK2,这边才重新发2包而且Tcp通常就是设计为Seq大于丢包的序号,3次才认为丢包(可能可以改但查资料说通常3次)。如果这时候我这里的某个包比如5超时了,那我这边还得重新把3、4和5包发送过来,但这边已经有3和4包了,就会产生冗余。
4. TCP超时计算是RTOx2,这样连续丢三次包就变成RTOx8了。这样如果再需要帧同步比如(moba,fps)对网络要求高的游戏来讲是致命的。
5.好多参数不好设置,比如重发多少次认为连接死掉了

说了一堆Tcp的事情(有点跑题),接下来可以衬托出kcp的优点。Kcp的使用方式框架如下:

 数据结构如下:

 可以看出,他这边自己实现一套粘包功能了。有了这个cmd就可以另sn可以是Seq也可以是Ack。算法里还有一些其他参数:

 这些是重传时的参数,他下次重传时间会越来越大,但比起Tcp要小的多。

 发送图解:

 

 代码逻辑:

 

 

 可以看出:他是先把应用层的据塞进snd_queue,然后snd_buf根据当前的滑动窗口从snd_queue取得数据,然后进行数据的发送。

 这块可以看出他的拥塞窗口策略与Tcp是一样的,但这个可以自己改。

接受逻辑:

 代码如下:

 

 

 

可以看出当接受消息时,他会根据消息:

1.从Snd_buf剔除已经接受的消息

2.如果这条消息是新接受的,他会先放进rcv_buf,当条件满足时把消息加到rcv_queue里。他的每条消息的frg会从最大逐步减到0,比如一条完整的消息分成3个包,他的frg分别为2 1 0。

接受还会计算rto。

 

 算法与tcp一样。。可以自己定义

 

 这个Wask Wins命令与Tcp的信号消息一样,就是告知对方现在窗口还够用,可以继续发送。

 Kcp有个快速重传机制,比如 1 2 3 4 5 快重传为2,当我发送 1 2 3,对方返回 2 和 3 那么我就知道1已经被跳过去2次了,等下次再发送的时候1会重发。

 应用层从rcv_queue里捞数据读取即可。。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值