【編程之美】超時重傳,滑動窗口,可靠性傳輸原理C語言實現


版權聲明:本文為博主原創文章,遵循 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。進行重傳操作,並且啟動定時器。

 

當數據發送時長到達重傳生命周期定時器限值時,表示整包數據沒有在規定的時間里發送成功,此時關閉對應窗口的定時器,清空對應的窗口信息以及這一包數據的緩存信息,如果此時發現緩存數據中有數據需要發送,則進行發送,更新對應的窗口信息。

 

 

 

結果查看

 

 

 

 

 

 

 

 

 

 

 

推薦閱讀

(點擊標題可跳轉閱讀)

【編程之美】用C語言實現狀態機(實用)

【超詳細C語言】帶你吃透貪吃蛇游戲之精髓

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM