版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接: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),如下圖所示:

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個,整個滑窗整體往后滑動。
以上的傳輸過程請參考下圖:

ARQ傳輸機制原理圖
那么怎么實現呢?
對於單個數據包來說,是很簡單的,其ARQ狀態機如下

對於需要窗口傳輸的重傳機制來說,如圖所示,
發送窗口只有收到對端對於本段發送窗口內字節的ACK確認,才會移動發送窗口的左邊界。
接收端可以根據自己的狀況通告窗口大小,從而控制發送端的接收,進行流量控制。


我們來定義數據結構
//數據索引typedef struct {int status;int dataID;u16* Recvbuffer;int len;}CommDataIndex;
//包的狀態typedef struct{int id;int crc;}PacketStatus;
//發送狀態typedef struct{int dataID;int iRetries;int iResendTimerID;//重傳定時器int iCycleTimerID; //生命周期定時器}SendStatus;
//窗口信息typedef struct {int winSize;SendStatus status[wSize];int minID;int maxID;}WINMSG;
//數據類型enum{RTS=0,CTS,ACKNAK,payload,}DATATYPE;
數據結構定義如下圖,當有數據需要發送時,先申請動態內存緩存數據,最多緩存20包數據,數據的具體內容如CommDataIndex中所示,包含數據ID序號、數據buffer、數據長度;窗口的內容(包含窗口大小,窗口中每包數據發送狀態,窗口中最小最大的數據ID序號) 如winMsg所示;數據包發送狀態(包含數據ID,重發次數,重發定時器,生命周期定時器)如下Status所示。

發送端具體流程:
有數據發送時,先判斷CommDataIndex數組中是否有空的位置,如果為空則將數據放入,最多緩存20包數據;
判斷窗口是否滿狀態,當窗口不是滿狀態時,則可以發送數據,更新窗口信息(窗口中的最小最大ID,發送狀態),啟動發送狀態中的定時器、更新重傳次數;

當返回ack,則表示發送成功,判斷是哪一包發送成功,找到這一包在commDataIndex的位置,並且清空,釋放內存,找到這一包在窗口中的位置,清除這個位置窗口的發送狀態信息(重傳次數,數據ID),關閉這個窗口位置的定時器;如果這一包是窗口中最小的ID值,則可以移動窗口,進行下一包的傳輸,更新窗口中最小最大ID值
當返回nak,則表示發送失敗,判斷是哪一包發送失敗,找到這一包在winMsg的位置,關閉窗口中對應的定時器,此時進行重發,更新發送狀態status,更新重傳次數,並且開啟窗口中對應的定時器;當重傳次數超過4次時,關閉對應的定時器,清空窗口發送狀態的信息,重傳次數,數據ID,找到這一包在commDataIndex的位置,並且清空,釋放內存。
當沒有響應消息時,表示發送超時,窗口對應的定時器ID構造事件傳遞給通信任務的定時器事件接收ID中,在發送端事件ID中處理,首先關閉定時器,找到定時器對應的窗口的位置,更新對應窗口發送狀態信息,更新重傳次數,當重傳次數超過4次時,關閉對應的定時器,清空窗口發送狀態的信息,重傳次數,數據ID。進行重傳操作,並且啟動定時器。
當數據發送時長到達重傳生命周期定時器限值時,表示整包數據沒有在規定的時間里發送成功,此時關閉對應窗口的定時器,清空對應的窗口信息以及這一包數據的緩存信息,如果此時發現緩存數據中有數據需要發送,則進行發送,更新對應的窗口信息。

結果查看





推薦閱讀
(點擊標題可跳轉閱讀)

