計算機網絡 Exp1.數據鏈路層ARQ協議設計 Part 4.源程序

原實驗佈置中的第五部分因爲沒有什麼參考價值,所以略去,將第六部分作爲第四部分。

本實驗所有內容的傳送門
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();
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章