计算机网络项目——最小网元设计(阶段二)
目錄
- 階段目標(biāo)
- 設(shè)計描述
- 1、幀結(jié)構(gòu)
- 2、幀定位
- 3、差錯檢測
- 4、差錯控制
- 5、流量控制
- 6、長幀傳輸——分片
- 測試情況
- 其他想說的話
階段目標(biāo)
用鏈路層例程代碼(LnkTester.sln)設(shè)計實(shí)現(xiàn)鏈路層上點(diǎn)到點(diǎn)之間的通信過程,具體包括:兩點(diǎn)之間幀同步、、差錯檢測、差錯控制、簡單的流量控制。
設(shè)計描述
本階段主要停留在鏈路層對等實(shí)體之間的通信,故我們設(shè)計時出于簡便的測試的想法,先不考慮上下之間的數(shù)據(jù)傳遞帶來的封裝問題,將應(yīng)用層融合為鏈路層。不過需要修改配置文件,將PHY和APP兩層的配置文件改為PHY和LNK的兩層文件,主要就是改一個APP層的名字,沒有其他需求印象中是不太需要改其他參數(shù)的。
1、幀結(jié)構(gòu)
不管是幀、數(shù)據(jù)包還是什么其他的數(shù)據(jù)形式也好,設(shè)計之初對于幀格式的設(shè)計一定是首尾的,這是一切的基礎(chǔ)。在計算機(jī)網(wǎng)絡(luò)的發(fā)展史中,協(xié)議的產(chǎn)生常常會伴隨幀格式的改變。所以這是設(shè)計中的重頭戲。
PS:設(shè)計采用面向位填充首尾定界法
| 幀頭定界符 | ACK標(biāo)志位 | 幀序號 | 數(shù)據(jù)位 | CRC校驗位 | 幀尾定界符 |
2、幀定位
原理:
幀定位采用面向位填充首尾定界法。幀頭和幀尾標(biāo)志固定為‘01111110’,在發(fā)送方封裝成幀時通過get_frame()函數(shù)將幀內(nèi)連續(xù)5個1后添加一個0,以便能夠準(zhǔn)確對幀進(jìn)行定位。在接收方又通過讀取bit流,讀到首尾的“01111110”來進(jìn)行定界,然后去除數(shù)據(jù)位中的連續(xù)5個1后的0.來還原真實(shí)數(shù)據(jù)位。(U8類型=char類型,此處這樣定義,只是為了專門區(qū)別,用來表示單字節(jié)數(shù)據(jù),這是指導(dǎo)書的說明,但我自己使用的時候則感覺多把他看成是1bit數(shù)據(jù)的形式。)
涉及函數(shù):
int get_frame(U8* s, int len); //發(fā)送端成幀 int locate_frame(U8* s, int len,U8* bufSend); //接收端提取幀要點(diǎn):
3、差錯檢測
原理:
差錯檢測采用CRC-4來產(chǎn)生校驗碼緊跟數(shù)據(jù)后面,其中CRC生成多項式采用固定G[5] = { 1,0,0,1,1 }利用crccode()函數(shù)來產(chǎn)生四位校驗碼,并在接收端用crcdecode()函數(shù)進(jìn)行校驗。校驗錯誤則丟棄該幀,等待重傳。(此處實(shí)際情況好像是多采用檢錯能力更強(qiáng)的CRC16,此處出于簡便設(shè)計和運(yùn)算速度,采用最短的CRC校驗碼)
涉及函數(shù):
int crccode(U8* s, int len); //計算s的CRC校驗位采用生成多項式G[5]={1,0,0,1,1} bool crcdecode(U8* s, int len); //接收方校驗數(shù)據(jù)要點(diǎn):
4、差錯控制
原理:
對于數(shù)據(jù)在信道上進(jìn)行傳輸?shù)倪^程中可能產(chǎn)生的誤碼進(jìn)行差錯控制設(shè)計,我們采用停等協(xié)議進(jìn)行差錯控制(雖然后續(xù)階段的設(shè)計發(fā)現(xiàn),停等協(xié)議這種一發(fā)一收的方式還是不夠高效,但設(shè)計是最簡單的,其實(shí)可以嘗試滑窗的GB_N和SR協(xié)議),增加1位ACK標(biāo)識符和4位序列(由于是停等協(xié)議序號只有0000和1111),ACK標(biāo)志位判斷當(dāng)前幀是數(shù)據(jù)幀(0)還是確認(rèn)幀(1),并且判斷收到的是否為正確序號的幀(相應(yīng)確認(rèn)幀序號總是為期待收到的下一幀的序列號)。確認(rèn)幀的數(shù)據(jù)位采用八位全1(11111111)作為數(shù)據(jù)部分。為提高效率,收方收到ACK為1的幀數(shù)據(jù)位中判斷收到數(shù)據(jù)位中有超過四個1即判斷收到確認(rèn)幀;同時序號中有三個及三個以上相同數(shù)字的即自動糾錯判斷幀序號。在發(fā)生差錯和丟包時,利用Timeout()內(nèi)部的重傳函數(shù)將緩存下來的數(shù)據(jù)進(jìn)行定時幀重傳,直到接收到正確的確認(rèn)幀,然后計時器開始變量isTimeStart停止。
涉及函數(shù):
int add_ack_seq(U8* s, int len); //加入1位ACK標(biāo)識位和4位序列號——————確認(rèn)幀的ACK為1,并且ACK的數(shù)據(jù)bit流部分為11111111 bool isAck(U8* s); //判斷是否為確認(rèn)幀 int get_ack_frame(U8* s, U8* ack_frame); //生成s對應(yīng)的確認(rèn)幀(幀序號為期待下一次接收到的幀序號)---------------------重要變量-----------------------------超時重傳-- int TickTack = 0;//全局變量在Timeout()用來差錯控制中的重傳 bool isTimerStart = false;//全局計時器啟動標(biāo)志--緩存區(qū)-- U8* buffer = NULL;//全局的緩沖區(qū),發(fā)送前用來裝載可能重傳的數(shù)據(jù) int buflen = 0;//緩沖區(qū)儲存的字符串長度--幀序號-- U8 SEQ[4] = { 0,0,0,0 };//四位序列號為全局變量,只有0000和1111,停等協(xié)議中異或交替出現(xiàn)要點(diǎn):
報錯截圖:(這個困擾了我很久,網(wǎng)上只知道是釋放了空內(nèi)存,后來花費(fèi)很久才找到上面的解決辦法)
5、流量控制
在本例中,對于停等協(xié)議來說其實(shí)流量控制的意義不大,因為一發(fā)一收的機(jī)制本身就不會出現(xiàn)流量一股腦全懟進(jìn)去的情況。但還是可以做流量控制,可以仿照重傳,在Timeout()函數(shù)中設(shè)置一個閾值,和一個類似Ticktack的時鐘變量,然后當(dāng)達(dá)到閾值的時候,在sendtolower()前sleep一定的時間,達(dá)到流量控制的效果。(由于當(dāng)時有點(diǎn)摸魚,只隨手寫了個sleep,上面的說法才應(yīng)該是正確的流控)
6、長幀傳輸——分片
這是當(dāng)時自己沒有做的一項拓展功能,但在后面傳輸圖片或者文件的功能實(shí)現(xiàn)中,發(fā)現(xiàn)這是極其必要的。因為如果傳輸?shù)臄?shù)據(jù)過長,會導(dǎo)致數(shù)據(jù)產(chǎn)生錯誤而重傳的概率提高,可能導(dǎo)致反復(fù)重傳而傳不過去。因此將數(shù)據(jù)進(jìn)行分片多次傳輸,減小每次傳輸?shù)膸L度,就有必要了。
雖然沒有具體實(shí)現(xiàn),但后續(xù)想了一下實(shí)現(xiàn)思路,還是比較簡單:可以將數(shù)據(jù)按定長分段存入一個循環(huán)隊列中,然后對每個小幀進(jìn)行幀頭和幀長的記錄,然后依次封裝發(fā)送。可以用一個標(biāo)志變量記錄當(dāng)發(fā)送總數(shù)據(jù)量等于從上層接收到的幀長的時候,來表示一個完整的幀已經(jīng)傳輸成功;或者可以用更多的幀序號加以區(qū)分。接收方則需要按幀序號重新組裝數(shù)據(jù)放入一個新的內(nèi)存即可。
測試情況
1、點(diǎn)到點(diǎn)數(shù)據(jù)傳輸情況
發(fā)送方:
接收方:
2、重傳情況
發(fā)送方:
接收方:
其他想說的話
階段二應(yīng)該是后面兩個階段的基礎(chǔ),個人覺得階段二主要是學(xué)會如何閱讀源碼,然后學(xué)會如何使用已有的一些例程函數(shù),去方便我們的代碼編寫。(然后在調(diào)試過程中,最重要的就是學(xué)會看內(nèi)存數(shù)據(jù)的變化,這是很重要的一點(diǎn),能提高解決bug的效率)
不過好在這一階段的代碼,有老師的講解視頻可以參考,所以學(xué)著參考視頻,放開手大膽去做,克服畏難情緒,好的結(jié)果才能回應(yīng)這樣一個好的開端!
總結(jié)
以上是生活随笔為你收集整理的计算机网络项目——最小网元设计(阶段二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: js浮点运算式
- 下一篇: binarysearch java,ja