【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

本文链接:https://www.cnblogs.com/lihuidashen/p/12800323.html

微信链接:https://mp.weixin.qq.com/s/LMBklt3xyZR2mu076lguCg ;

最近在做无线传输的通信协议实现,这里涉及到超时重传,窗口移动,可靠性传输的问题,有些一些心得,也有很多的调试历程,与大家分享,当然源码不会开源,但是思想会毫无保留.

首先我们看重传原理ARQ

ARQ(AutomaticRepeat reQuest,自动重传请求)是在数据链路层(MAC)实现的一种可靠性传输机制。其重传原理是发送端先将数据链路层的数据SDU按照固定的ARQ块大小来进行分片,最后剩下的数据不管多小都分为一个分片,接着为每个分片进行编号BSN(Block Sequence Number),如下图所示:

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

ARQ机制中SDU分片和BSN示意图

随后确定发送端的ARQ滑窗,即:将第一个发送PDU块作为ARQ发送窗口的起始PDU块ARQ_TX_WINDOW_START,接下来发送的下一个PDU块作为ARQ发送下一个PDU块ARQ_TX_NEXT_BSN。当发送的1个或N个PDU块被接收端确认接收成功后,其滑窗的起始PDU块ARQ_TX_WINDOW_START会加1或N,即:往后面滑1或N格,而下一个PDU块始终指到未发送PDU块的第1个。

同时,接收端也会根据接收情况来确定接收ARQ滑窗,即:通过ARQ接收起始PDU块ARQ_RX_WINDOW_START和接收ARQ滑窗大小ARQ_RX_WINDOW_SIZE两个参数来确定该滑窗大小。当在滑窗内的PDU块全部接收到后,会确认这次接收PDU的结果,如果有个别PDU块接收失败,则在后续的反馈IE中指出来,让发送端进行重传,重传最大次数来控制重传次数,否则就直接将接收ARQ滑窗往后滑,ARQ接收起始PDU块ARQ_RX_WINDOW_START会滑到还未接收PDU块的第1个,整个滑窗整体往后滑动。

以上的传输过程请参考下图:

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

ARQ传输机制原理图

那么怎么实现呢?

对于单个数据包来说,是很简单的,其ARQ状态机如下

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

对于需要窗口传输的重传机制来说,如图所示,

发送窗口只有收到对端对于本段发送窗口内字节的ACK确认,才会移动发送窗口的左边界。
接收端可以根据自己的状况通告窗口大小,从而控制发送端的接收,进行流量控制。

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

我们来定义数据结构

<span class="code-snippet_outer"><span class="code-snippet__keyword">typedef <span class="code-snippet__class"><span class="code-snippet__keyword">struct {</span></span></span></span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int status;</span></span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int dataID;</span></span>
<span class="code-snippet_outer">  u16* Recvbuffer;</span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int len;</span></span>
<span class="code-snippet_outer">}CommDataIndex;</span>
<span class="code-snippet_outer"><span class="code-snippet__keyword">typedef <span class="code-snippet__class"><span class="code-snippet__keyword">struct{</span></span></span></span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int id;</span></span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int crc;</span></span>
<span class="code-snippet_outer">}PacketStatus;</span>
<span class="code-snippet_outer"><span class="code-snippet__keyword">typedef <span class="code-snippet__class"><span class="code-snippet__keyword">struct{</span></span></span></span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int dataID;</span></span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int iRetries;</span></span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int iResendTimerID;</span></span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int iCycleTimerID; </span></span>
<span class="code-snippet_outer">}SendStatus;</span>
<span class="code-snippet_outer"><span class="code-snippet__keyword">typedef <span class="code-snippet__class"><span class="code-snippet__keyword">struct {</span></span></span></span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int winSize;</span></span>
<span class="code-snippet_outer">  SendStatus status[wSize];</span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int minID;</span></span>
<span class="code-snippet_outer">  <span class="code-snippet__keyword">int maxID;</span></span>
<span class="code-snippet_outer">}WINMSG;</span>
<span class="code-snippet_outer"><span class="code-snippet__keyword">enum</span></span>
<span class="code-snippet_outer">{</span>
<span class="code-snippet_outer">  RTS=<span class="code-snippet__number">0,</span></span>
<span class="code-snippet_outer">  <span class="code-snippet__built_in">CTS,</span></span>
<span class="code-snippet_outer">  ACKNAK,</span>
<span class="code-snippet_outer">  payload,</span>
<span class="code-snippet_outer">}DATATYPE;</span>

数据结构定义如下图,当有数据需要发送时,先申请动态内存缓存数据,最多缓存20包数据,数据的具体内容如CommDataIndex中所示,包含数据ID序号、数据buffer、数据长度;窗口的内容(包含窗口大小,窗口中每包数据发送状态,窗口中最小最大的数据ID序号) 如winMsg所示;数据包发送状态(包含数据ID,重发次数,重发定时器,生命周期定时器)如下Status所示。

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

发送端具体流程:

有数据发送时,先判断CommDataIndex数组中是否有空的位置,如果为空则将数据放入,最多缓存20包数据;

判断窗口是否满状态,当窗口不是满状态时,则可以发送数据,更新窗口信息(窗口中的最小最大ID,发送状态),启动发送状态中的定时器、更新重传次数;

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

当返回ack,则表示发送成功,判断是哪一包发送成功,找到这一包在commDataIndex的位置,并且清空,释放内存,找到这一包在窗口中的位置,清除这个位置窗口的发送状态信息(重传次数,数据ID),关闭这个窗口位置的定时器;如果这一包是窗口中最小的ID值,则可以移动窗口,进行下一包的传输,更新窗口中最小最大ID值

当返回nak,则表示发送失败,判断是哪一包发送失败,找到这一包在winMsg的位置,关闭窗口中对应的定时器,此时进行重发,更新发送状态status,更新重传次数,并且开启窗口中对应的定时器;当重传次数超过4次时,关闭对应的定时器,清空窗口发送状态的信息,重传次数,数据ID,找到这一包在commDataIndex的位置,并且清空,释放内存。

当没有响应消息时,表示发送超时,窗口对应的定时器ID构造事件传递给通信任务的定时器事件接收ID中,在发送端事件ID中处理,首先关闭定时器,找到定时器对应的窗口的位置,更新对应窗口发送状态信息,更新重传次数,当重传次数超过4次时,关闭对应的定时器,清空窗口发送状态的信息,重传次数,数据ID。进行重传操作,并且启动定时器。

当数据发送时长到达重传生命周期定时器限值时,表示整包数据没有在规定的时间里发送成功,此时关闭对应窗口的定时器,清空对应的窗口信息以及这一包数据的缓存信息,如果此时发现缓存数据中有数据需要发送,则进行发送,更新对应的窗口信息。

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

结果查看

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

推荐阅读

(点击标题可跳转阅读)

Original: https://www.cnblogs.com/lihuidashen/p/12800323.html
Author: 技术让梦想更伟大
Title: 【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/535148/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球