一.实验目的
实现一个滑动窗口协议的数据传送部分,目的在于使学生更好地理解基本滑动窗口协议的基本工作原理,掌握计算机网络协议的基本实现技术。
二.实验原理
(1)窗口机制
滑动窗口协议的基本原理就是在任意时刻,发送方都维持了一个连续的允许发送的帧的序号,称为发送窗口;同时,接收方也维持了一个连续的允许接收的帧的序号,称为接收窗口。发送窗口和接收窗口的序号的上下界不一定要一样,甚至大小也可以不同。不同的滑动窗口协议窗口大小一般不同。发送方窗口内的序列号代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧。
(2)1比特滑动窗口协议
当发送窗口和接收窗口的大小固定为1时,滑动窗口协议退化为停等协议(stop-and-wait)。该协议规定发送方每发送一帧后就要停下来,等待接收方已正确接收的确认(acknowledgement)返回后才能继续发送下一帧。由于接收方需要判断接收到的帧是新发的帧还是重新发送的帧,因此发送方要为每一个帧加一个序号。由于停等协议规定只有一帧完全发送成功后才能发送新的帧,因而只用一比特来编号就够了。
(3)后退n协议
由于停等协议要为每一个帧进行确认后才继续发送下一帧,大大降低了信道利用率,因此又提出了后退n协议。后退n协议中,发送方在发完一个数据帧后,不停下来等待应答帧,而是连续发送若干个数据帧,即使在连续发送过程中收到了接收方发来的应答帧,也可以继续发送,且发送方在每发送完一个数据帧时都要设置超时定时器,只要在所设置的超时时间内仍收到确认帧,就要重发相应的数据帧。如:当发送方发送了N个帧后,若发现该N帧的前一个帧在计时器超时后仍未返回其确认信息,则该帧被判为出错或丢失,此时发送方就不得不重新发送出错帧及其后的N帧。
三.实验步骤
1.编写滑动窗口协议的实现程序;
2.在模拟实现,调试并运行自己编写的协议实现程序;
3.了解协议的工作轨迹,如出现异常情况,在实验报告中写出原因分析。
四.实验报告
1.详细描述实验过程。
2.实现具体的窗口滑动协议。
实验在Fedora 16 Linux下完成。实验模拟了滑动窗口协议,服务器向客户端发送数据,客户端判断数据是否错误,错误就重传错误的帧。
主要代码如下,包含一个客户端文件和服务器文件:
/* **************************************************************** * 文件名: client.c * 描述: 本文件用于模拟滑窗协议客户端 * 编译: Linux: gcc -lm -o client client.c * 可执行文件: ./client * 作者: 段聪 01091138 ******************************************************************/ #include<stdio.h> #include<string.h> #include<sys/socket.h> #include<sys/types.h> #include<netinet/in.h> #include<arpa/inet.h> #define server_ipaddr "127.0.0.1" /************************************************************************* * 函数名: main * 参数: NIL * 返回值: NIL * 描述: 主函数 ***************************************************************************/ main() { int std, lfd, len, choice, cport; char str[20], str1[20], err[20]; struct sockaddr_in saddr, caddr; printf("输入端口号:"); scanf("%d", &cport); std = socket(AF_INET, SOCK_STREAM, 0); if(std<0) perror("创建socket错误"); bzero(&saddr, sizeof(saddr)); saddr.sin_family = AF_INET; inet_pton(AF_INET, server_ipaddr, &saddr.sin_addr); saddr.sin_port = htons(cport); connect(std, (struct sockaddr *)&saddr, sizeof(saddr)); while(1) { read(std, str, 20); if(strcmp(str, "Exit") == 0) { printf("正在退出!\n"); break; } printf("收到: %s 错误?(1 - 是 0 - 否): ", str); scanf("%d", &choice); if(choice == 0)write(std, "-1", sizeof("-1")); else { printf("输入发生错误序列的帧号:"); scanf("%s", err); write(std, err, sizeof(err)); read(std, str, 20); printf("收到传输帧: %s\n", str); } } close(std); }
/* **************************************************************** * 文件名: server.c * 描述: 本文件用于模拟滑窗协议服务器端 * 编译: Linux: gcc -o server server.c * 可执行文件: ./server * 作者: 段聪 01091138 ******************************************************************/ #include<sys/types.h> #include<netinet/in.h> #include<arpa/inet.h> //滑动窗口大小 #define SIZE 4 /************************************************************************* * 函数名: main * 参数: NIL * 返回值: NIL * 描述: 主函数 ***************************************************************************/ main() { int std, lfd, len, i, j, status, sport; char str[20], frame[20], temp[20], ack[20]; struct sockaddr_in saddr, caddr; printf("输入端口号:"); scanf("%d", &sport); std = socket(AF_INET, SOCK_STREAM, 0); if(std<0) perror("创建socket错误"); bzero(&saddr, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_ANY); saddr.sin_port = htons(sport); lfd = bind(std, (struct sockaddr *)&saddr, sizeof(saddr)); if(lfd) perror("绑定错误"); listen(std, 5); len = sizeof(&caddr); lfd = accept(std, (struct sockaddr *)&caddr, &len); printf("输入要发送的文本:"); scanf("%s", str);i = 0; while(i<strlen(str)) { memset(frame, 0, 20); strncpy(frame, str+i, SIZE); printf("\n传输帧:"); len = strlen(frame); for(j=0; j<len; j++) { printf("%d", i+j); sprintf(temp, "%d", i+j); strcat(frame, temp); } write(lfd, frame, sizeof(frame)); read(lfd, ack, 20); sscanf(ack, "%d", &status); if(status == -1) printf("\n传输成功!"); else { printf("接收错误: %d", status); printf("\n重传帧:"); frame[0] = str[status]; frame[1] = '\0';write(lfd, frame, sizeof(frame)); } i = i + SIZE; } write(lfd, "Exit", sizeof("Exit")); printf("\n正在退出!\n"); sleep(2); close(lfd); close(std); }
使用gcc进行编译:
gcc -o client client.c gcc -o server server.c
产生两个可执行文件client 和server。
运行程序