◼ ACK机制 (用来确认对方到底有没有收到)
◼ 重传机制 (在协议栈不会重传)
◼ 序号机制 3 2 1 -》2 3 1 (先发的包不一定最先到达)
◼ 重排机制 2 3 1 ->3 2 1 (将收到的包进行重组)
◼ 窗口机制
Tcp不用我们管
可靠性udp 5种机制都需要用户层处理
udp场景
1)实时性要求: 音视频的通话 、 游戏(王者荣耀) 。
2)节省资源的要求: 嵌入式设备(电池供电) , 手机状态报告 、户外设备
UDP在传输的时候buffer一定要小于MTU。MT大小标准是1500
sendto(user data) 运营商头 + userdata + udp头(8字节) + IP头(20字节) = 1500-8-20=1472 sento一般来讲最大1400字节
udp是报文传输,必须一次性读完。如果不读完,剩余部分就会被丢弃。TCP是流式传输,可以分块来读取数据。
在网络中,我们认为传输是不可靠的,而在很多场景下我们需要的是可靠的数据, 所谓的可靠,指的是数据能够正常收到,且能够顺序收到,于是就有了ARQ协议, TCP之所以可靠就是基于此。
ARQ协议(Automatic Repeat-reQuest),即自动重传请求,是传输层的错误纠正协 议之一,它通过使用确认和超时两个机制,在不可靠的网络上实现可靠的信息 传输。ARQ协议主要有3种模式:
停等协议的工作原理如下: 1、发送方对接收方发送数据包,然后等待接收方回复ACK并且开始计时。 2、在等待过程中,发送方停止发送新的数据包。 3、当数据包没有成功被接收方接收,接收方不会发送ACK.这样发送方在等待一 定时间后,重新发送数据包。 4、反复以上步骤直到收到从接收方发送的ACK.
为了克服停等协议长时间等待ACK的缺陷,连续ARQ协议会连续发送一组数据包,然后再 等待这些数据包的ACK。
什么是滑动窗口:发送方和接收方都会维护一个数据帧的序列,这个序列被称作窗口。发送方的 窗口大小由接收方确定,目的在于控制发送速度,以免接收方的缓存不够大,而导致溢出,同时控 制流量也可以避免网络拥塞。协议中规定,对于窗口内未经确认的分组需要重传。
回退N步(Go-Back-N,GBN):回退N步协议允许发送方在等待超时的间歇,可以继续发送分 组。所有发送的分组,都带有序号。在GBN协议中,发送方需响应以下三种事件: 1、上层的调用。上层调用相应send()时,发送方首先要检查发送窗口是否已满。 2、接收ACK。在该协议中,对序号为n的分组的确认采取累积确认的方式,表明接收方已 正确接收到序号n以前(包括n)的所有分组。 3、超时。若出现超时,发送方将重传所有已发出但还未被确认的分组对于接收方来说,若一个序号为n的分组被正确接收,并且按序,则接收方会为该分组返 回一个ACK给发送方,并将该分组中的数据交付给上层。在其他情况下,接收方都会丢弃 分组。若分组n已接收并交付,那么所有序号比n小的分组也已完成了交付。因此GBN采用 累积确认是一个很自然的选择。发送方在发完一个窗口里的所有分组后,会检查最大的有 效确认,然后从最大有效确认的后一个分组开始重传。
虽然GBN改善了停等协议中时间等待较长的缺陷,但它依旧存在着性能问题。特别是当窗口长度很大的时候,会使效率大大降低。而SR协议通过让发送方仅重传在接收方丢失或损坏了的分组,从而避免了不必要的重传,提高了效率。在SR协议下,发送方需响应以下三种事件:
1、从上层收到数据。当从上层收到数据后,发送方需检查下一个可用于该分组的 序号。若序号在窗口中则将数据发送。 2、接收ACK。若收到ACK,且该分组在窗口内,则发送方将那个被确认的分组标记 为已接收。若该分组序号等于基序号,则窗口序号向前移动到具有最小序号的未确 认分组处。若窗口移动后并且有序号落在窗口内的未发送分组,则发送这些分组。 3、超时。若出现超时,发送方将重传已发出但还未确认的分组。与GBN不同的是, SR协议中的每个分组都有独立的计时器。
在SR协议下,接收方需响应以下三种事件:
(假设接收窗口的基序号为4,分组长度也为4) 1、序号在[4,7]内的分组被正确接收。该情况下,收到的分组落在接收方的窗口内,一个ACK 将发送给发送方。若该分组是以前没收到的分组,则被缓存。若该分组的序号等于基序号4, 则该分组以及以前缓存的序号连续的分组都交付给上层,然后,接收窗口将向前移动。 2、序号在[0,3]内的分组被正确接收。在该情况下,必须产生一个ACK,尽管该分组是接收方 以前已确认过的分组。若接收方不确认该分组,发送方窗口将不能向前移动。 3、其他情况。忽略该分组 对于接收方来说,若一个分组正确接收而不管其是否按序,则接收方会为该分组返回一个ACK 给发送方。失序的分组将被缓存,直到所有丢失的分组都被收到,这时才可以将一批分组按 序交付给上层。
RTO(Retransmission TimeOut)即重传超时时间。
RTT(Round-Trip Time): 往返时延。表示从发送端发送数据开始,到发送端收到来自 接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延。 由三部分组成: ◼ 链路的传播时间(propagation delay) ◼ 末端系统的处理时间、 ◼ 路由器缓存中的排队和处理时间(queuing delay) 其中,前两个部分的值对于一个TCP连接相对固定,路由器缓存中的排队和处理时间会 随着整个网络拥塞程度的变化而变化。 所以RTT的变化在一定程度上反应网络的拥塞程度。
双方在通信的时候,发送方的速率与接收方的速率是不一定相等,如果发送方 的发送速率太快,会导致接收方处理不过来,这时候接收方只能把处理不过来 的数据存在缓存区里(失序的数据包也会被存放在缓存区里)。
如果缓存区满了发送方还在疯狂着发送数据,接收方只能把收到的数据包丢掉, 大量的丢包会极大着浪费网络资源,因此,我们需要控制发送方的发送速率, 让接收方与发送方处于一种动态平衡才好。
对发送方发送速率的控制,称之为流量控制。
接收方每次收到数据包,可以在发送确定报文的时候,同时告诉发送方自己的缓存区还剩 余多少是空闲的,我们也把缓存区的剩余大小称之为接收窗口大小,用变量win来表示接 收窗口的大小。
发送方收到之后,便会调整自己的发送速率,也就是调整自己发送窗口的大小,当发送方 收到接收窗口的大小为0时,发送方就会停止发送数据,防止出现大量丢包情况的发生。
当发送方停止发送数据后,该怎样才能知道自己可以继续发送数据?
1. 当接收方处理好数据,接受窗口 win > 0 时,接收方发个通知报文去通知发送方,告诉他可以继续发 送数据了。当发送方收到窗口大于0的报文时,就继续发送数据。
2. 当发送方收到接受窗口 win = 0 时,这时发送方停止发送报文,并且同时开启一个定时器,每隔一段 时间就发个测试报文去询问接收方,打听是否可以继续发送数据了,如果可以,接收方就告诉他此时 接受窗口的大小;如果接受窗口大小还是为0,则发送方再次刷新启动定时器。
1. 通信的双方都拥有两个滑动窗口,一个用于接受数据,称之为接收窗口;一个用于 发送数据,称之为拥塞窗口(即发送窗口)。指出接受窗口大小的通知我们称之为窗口 通告。
2. 接收窗口的大小固定吗?接受窗口的大小是根据某种算法动态调整的。
3. 接收窗口越大越好吗?当接收窗口达到某个值的时候,再增大的话也不怎么会减少 丢包率的了,而且还会更加消耗内存。所以接收窗口的大小必须根据网络环境以及 发送发的的拥塞窗口来动态调整。
4. 发送窗口和接受窗口相等吗?接收方在发送确认报文的时候,会告诉发送发自己的 接收窗口大小,而发送方的发送窗口会据此来设置自己的发送窗口,但这并不意味 着他们就会相等。首先接收方把确认报文发出去的那一刻,就已经在一边处理堆在 自己缓存区的数据了,所以一般情况下接收窗口 >= 发送窗口。
拥塞控制和流量控制虽然采取的动作很相似,但拥塞控制与网络的拥堵情况相关联,而流量控制与接收方的缓存状态相关联。
RTO(超时重传时间)
减少应答包的数量
用户数据:应用层发送的数据,如一张图片2Kb的数据
MTU:最大传输单元。即每次发送的最大数据
RTO:Retransmission TimeOut,重传超时时间。
cwnd:congestion window,拥塞窗口,表示发送方可发送多少个KCP数据包。 与接收方窗口有关,与网络状况(拥塞控制)有关,与发送窗口大小有关。
rwnd:receiver window,接收方窗口大小,表示接收方还可接收多少个KCP数据包
snd_queue:待发送KCP数据包队列
snd_nxt:下一个即将发送的kcp数据包序列号
snd_una:下一个待确认的序列号
1. 创建 KCP对象:ikcpcb *kcp = ikcp_create(conv, user);
通过一个唯一标识符表示一个双方通信的通道。 (会话ID)
2. 设置传输回调函数(如UDP的send函数):kcp->output = udp_output; 1. 真正发送数据需要调用sendto
3. 循环调用 update:ikcp_update(kcp, millisec);
4. 输入一个应用层数据包(如UDP收到的数据包): ikcp_input(kcp,received_udp_packet,received_udp_size); 1. 我们要使用recvfrom接收,然后扔到kcp里面做解析
5. 发送数据:ikcp_send(kcp1, buffer, 8); 用户层接口
6. 接收数据:hr = ikcp_recv(kcp2, buffer, 10); 用户层读取数据
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字节
4. 最小RTO:不管是 TCP还是 KCP计算 RTO时都有最小 RTO的限制,即便计算出来RTO为 40ms,由于默认的 RTO是100ms,协议只有在100ms后才能检测到丢包,快速模式下为 30ms,可以手动更改该值: kcp->rx_minrto = 10;
conv:连接号。UDP是无连接的,conv用于表示来自于哪个 客户端。对连接的一种替代
cmd:命令字。如,IKCP_CMD_ACK确认命令, IKCP_CMD_WASK接收窗口大小询问命令, IKCP_CMD_WINS接收窗口大小告知命令,
frg:分片,用户数据可能会被分成多个KCP包,发送出去
frg : 从高到低
wnd:接收窗口大小,发送方的发送窗口不能超过接收方 给出的数值
ts:时间序列
sn:序列号
una:下一个可接收的序列号。其实就是确认号,收到 sn=10的包,una为11
len:数据长度
data:用户数据