原實驗佈置中的第五部分因爲沒有什麼參考價值,所以略去,將第六部分作爲第四部分。
本實驗所有內容的傳送門
Part 1.實驗環境與軟件設計
Part 2.實驗結果的理論分析與實測
Part 3.研究與探索的問題
Part 4.源程序
5.源程序文件
datalink.h
/* FRAME kind */
#define FRAME_DATA 1
#define FRAME_ACK 2
#define FRAME_NAK 3
/*
DATA/ACK/NAK Frame
+=========+========+========+===============+========+
| KIND(1) | SEQ(1) | ACK(1) | DATA(240~256) | CRC(4) |
+=========+========+========+===============+========+
*/
datalink.c
#include <stdio.h>
#include <string.h>
#include "protocol.h"
#include "datalink.h"
//發出一幀後等待ack來的時間
#define DATA_TIMER 3000
//收到一幀後等待單獨發送ack的時間
#define ACK_TIMER 1100
//最大的序列號seq,發送和接收窗口的總大小爲MAX_SEQ+1
#define MAX_SEQ 31
//接受窗口和發送窗口的大小,可索引範圍是[0,NR_BUFS-1]
#define NR_BUFS ((MAX_SEQ+1)/2)
//信道上沒有NAK的標誌,保證信道上只有至多一個NAK
static int no_nak = 1;
//物理層準備好的標誌
static int phl_ready = 0;
struct FRAME {
unsigned char kind;
unsigned char ack;
unsigned char seq;
unsigned char data[PKT_LEN];//注意data不能直接賦值,因爲是數組
unsigned int padding;
};
//發送、接收窗口的緩衝區
static unsigned char in_buf[MAX_SEQ + 1][PKT_LEN];
static unsigned char out_buf[MAX_SEQ + 1][PKT_LEN];
static void inc(unsigned char* pnum) {
//這裏一定要加括號
(*pnum)++;
if (*pnum == MAX_SEQ + 1)
* pnum = 0;
}
static int between(unsigned char a, unsigned char b, unsigned char c) {
//判斷是否滿足a<=b<c或其循環
//將來用來比較b幀是否在a和c(不含)之間
return ((a <= b) && (b < c)) || ((c < a) && (a <= b)) || ((b < c) && (c < a));
}
//接受一個幀,加上校驗值併發送
static void put_frame(unsigned char* frame, int len) {
//給幀增加4個byte的校驗值
*(unsigned int*)(frame + len) = crc32(frame, len);
send_frame(frame, len + 4);
//剛發送一幀時,物理層暫時不可用
phl_ready = 0;
}
//從out_buf取幀
//取決於frame_kind,發送幾種類型的幀:普通幀、ACK幀、NAK幀
static void send_data_frame(unsigned char frame_kind, unsigned char frame_nr, unsigned char frame_expected) {
struct FRAME s = { 0 };
s.kind = frame_kind;
s.seq = frame_nr;
//rhs的結果是frame_expected在[0,MAX_SEQ]域上的前一個
s.ack = (frame_expected + MAX_SEQ) % (MAX_SEQ + 1);
switch (frame_kind) {
case FRAME_DATA:
//從out_buf拷貝數據
memcpy(s.data, out_buf[frame_nr % NR_BUFS], PKT_LEN);
//發送,計時,輸出
put_frame((unsigned char*)& s, PKT_LEN + 3);
start_timer(frame_nr % NR_BUFS, DATA_TIMER);
dbg_frame("Send DATA %d %d, ID %d\n", s.seq, s.ack, *(short*)s.data);
break;
case FRAME_ACK:
//data不寫入數據
put_frame((unsigned char*)& s, PKT_LEN + 3);
dbg_frame("Send ACK %d\n", s.ack);
break;
case FRAME_NAK:
//信道上有了NAK,“沒有NAK”的標識no_nak置false
no_nak = 0;
put_frame((unsigned char*)& s, 3 + PKT_LEN);
dbg_frame("Send NAK %d\n", s.ack);
break;
}
//物理層有幀發送,暫時不可用,讓物理層自己通知可用
phl_ready = 0;
//已經發出了一幀,結束收到一幀後爲了捎帶確認而等待的計時器
stop_ack_timer();
}
int main(int argc, char** argv) {
int event, arg;
struct FRAME f = { 0 };
int len = 0;
int i;
//發送窗口的上下界[)
static unsigned char ack_expected = 0;
static unsigned char frame_nr = 0;
//發送窗口已經緩衝的幀數
static unsigned char nbuffered = 0;
//接收窗口的上下界[)
static unsigned char frame_expected = 0;
static unsigned char too_far = NR_BUFS;
//接收窗口每個槽的使用情況
static int arrived[NR_BUFS] = { 0 };
protocol_init(argc, argv);
lprintf("Designed by Lupinus_Linn, build: " __DATE__" "__TIME__"\n");
enable_network_layer();
while (1) {
//找物理層要事件
event = wait_for_event(&arg);
switch (event) {
//網絡層有幀要發送時
case NETWORK_LAYER_READY:
nbuffered++;
//發送幀,即將發送的上界frame_nr右移
get_packet(out_buf[frame_nr % NR_BUFS]);
send_data_frame(FRAME_DATA, frame_nr, frame_expected);
inc(&frame_nr);
break;
//由物理層通知信道可用
case PHYSICAL_LAYER_READY:
phl_ready = 1;
break;
//收到來幀
case FRAME_RECEIVED:
len = recv_frame((unsigned char*)& f, sizeof(f));
//幀損壞
if (len < 5 || crc32((unsigned char*)& f, len) != 0) {
dbg_event("**** Receiver Error, Bad CRC Checksum\n");
//信道上沒有NAK時才發送NAK,保證信道上同時只有一個NAK
//如果已經有NAK了,那就等到對方的發送定時器超時重傳
//frame_nr隨便填,不會看的
if (no_nak) send_data_frame(FRAME_NAK, 0, frame_expected);
break;
}
switch (f.kind) {
//收到了普通幀
case FRAME_DATA:
dbg_frame("Recv DATA %d %d, ID %d\n", f.seq, f.ack, *(short*)f.data);//打印收到幀完好
//順序不對但是幀是好的,先收下來
//額外發送NAK給對方表示不是想要的
//對方收到NAK(NAK實際上確認frame_expected前面的幀)後,重發seq=frame_expected的幀
if (f.seq != frame_expected && no_nak) {
send_data_frame(FRAME_NAK, 0, frame_expected);
}
else {
//現在不能發NAK,等會再單獨發ack確認前面的
start_ack_timer(ACK_TIMER);
}
//如果收到的seq在[frame_expected,too_far)之間(可能序號對也可能錯)
//並且存儲位是空閒的,就收下來
if (between(frame_expected, f.seq, too_far) && (arrived[f.seq % NR_BUFS] == 0)) {
arrived[f.seq % NR_BUFS] = 1;
memcpy(in_buf[f.seq % NR_BUFS], f.data, len - 7);
//如果接收的幀剛好是按序的
//或者新的幀剛好插入到前面丟失的空裏使得連續
//就全部提交(窗口滑動)
while (arrived[frame_expected % NR_BUFS]) {
put_packet(in_buf[frame_expected % NR_BUFS], len - 7);
//表示新收到一幀時信道上沒有NAK了
no_nak = 1;
//接受槽可用了
arrived[frame_expected % NR_BUFS] = 0;
//接收窗口的可用大小增加了
//注意接收窗口是個循環隊列,too_far是邏輯上的隊尾
//其值可能比frame_expected小
inc(&too_far);
inc(&frame_expected);
//單獨的ACK計時器開始計時,如果有捎帶確認,那麼會在發送時被停掉
//如果長時間沒有發送確認,就單獨發ACK幀
start_ack_timer(ACK_TIMER);
}
}
break;
case FRAME_NAK:
//這裏一定要加括號,==的優先級很低
//如果收到的是NAK,而且對方的frame_expected屬於我方發送窗口[ack_expcted,frame_nr)內
if (between(ack_expected, (f.ack + 1) % (MAX_SEQ + 1), frame_nr)) {
//打印NAK信息
dbg_frame("Recv NAK %d\n", f.ack);
//重傳對方需要的幀
send_data_frame(FRAME_DATA, (f.ack + 1) % (MAX_SEQ + 1), frame_expected);
}
break;
case FRAME_ACK:
if (between(ack_expected, (f.ack + 1) % (MAX_SEQ + 1), frame_nr)) {
//如果收到了單獨的ACK幀
dbg_frame("Recv ACK %d\n", f.ack);
}
break;
}
//根據對方的ACK滑動發送窗口
while (between(ack_expected, f.ack, frame_nr)) {
nbuffered--;
//停掉重傳定時器
stop_timer(ack_expected % NR_BUFS);
//!!必須先停前面的再增加
inc(&ack_expected);
}
break;
//重傳定時器超時
case DATA_TIMEOUT:
dbg_event("---- DATA %d timeout\n", arg);
send_data_frame(FRAME_DATA, ack_expected, frame_expected);
break;
//總是不來幀好發捎帶確認,要發單獨的ACK了
case ACK_TIMEOUT:
dbg_event("---- ACK %d timeout\n", arg);
send_data_frame(FRAME_ACK, 0, frame_expected);
break;
}
if (nbuffered < NR_BUFS && phl_ready)
enable_network_layer();
else
disable_network_layer();
}
}