• 1 滑动窗口协议实验 1.1 实验目的 计算机网络的数据链路层协议保证通信双方在有差错的通信线路上进 行无差错的数据传输,是计算机网络各层协议中通信控制功能最典型的一种 协议。 本实验实现一个数据链路层协议的...
  • 实现一个滑动窗口协议的数据传送部分,目的在于使学生更好地理解基本滑动窗口协议的基本工作原理,掌握计算机网络协议的基本实现技术。 二.实验原理 (1)窗口机制 滑动窗口协议的基本原理就是在任意时刻,...

    一.实验目的

    实现一个滑动窗口协议的数据传送部分,目的在于使学生更好地理解基本滑动窗口协议的基本工作原理,掌握计算机网络协议的基本实现技术。

    二.实验原理

    1)窗口机制

    滑动窗口协议的基本原理就是在任意时刻,发送方都维持了一个连续的允许发送的帧的序号,称为发送窗口;同时,接收方也维持了一个连续的允许接收的帧的序号,称为接收窗口。发送窗口和接收窗口的序号的上下界不一定要一样,甚至大小也可以不同。不同的滑动窗口协议窗口大小一般不同。发送方窗口内的序列号代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧。

    21比特滑动窗口协议

    当发送窗口和接收窗口的大小固定为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

    运行程序



    展开全文
  • 北邮 计算机网络课程实验 滑动窗口协议 选择重传协议与go-back-n协议 压缩包内附实验报告与配套代码
  • 计算机网络滑动窗口协议实验,包括停等、回退N和选择重传,实验平台为NetRiver,已经通过测试。
  • 滑动窗口协议实验

    2014-04-21 18:48:17
    #include "sysinclude.h" #include extern void SendFRAMEPacket(unsigned char* pData, unsigned int len); #define WINDOW_SIZE_STOP_WAIT 1 #define WINDOW_SIZE_BACK_N_FRAME 4 bool fullWin = false;...


    //NetRiver平台上的一个实验,用c语言实验1比特滑动窗口协议、回退N帧协议和选择性重传协议
    //使用队列实现
    #include "sysinclude.h"
    #include <deque>
    
    extern void SendFRAMEPacket(unsigned char* pData, unsigned int len);
    
    #define WINDOW_SIZE_STOP_WAIT 1
    #define WINDOW_SIZE_BACK_N_FRAME 4
    
    bool fullWin = false;
    
    typedef enum {data, ack, nak} frame_kind;
    typedef struct frame
    {
    	frame_kind kind;	//帧类型
    	unsigned int seq;	//序列号
    	unsigned int ack;	//确认号
    	unsigned char data[100];	//数据	
    };
    
    typedef struct Buffer {	//窗口中的帧
    	frame *pframe;
    	unsigned int size;
    };
    int back_n_frame_win = 0;
    int choice_frame_resend_win = 0;
    deque<Buffer> queue;
    
    /*
    * 停等协议测试函数
    */
    int stud_slide_window_stop_and_wait(char *pBuffer, int bufferSize, UINT8 messageType)
    {
    	Buffer buffer;
    	switch(messageType) {
    		case MSG_TYPE_TIMEOUT: {	//某个帧超时
    			buffer = queue.front();
    			int serial;
    			serial = ntohl(*(unsigned int *)pBuffer);
    			//if(serial == ntohl(buffer.pframe->seq))
    				SendFRAMEPacket((unsigned char *)buffer.pframe, buffer.size);
    			break;
    		}
    		case MSG_TYPE_SEND: {		//系统要发送一个帧
    			buffer.pframe = new frame;
    			*buffer.pframe = *(frame *)pBuffer;
    			buffer.size = bufferSize;
    			queue.push_back(buffer);	//放入队尾
    			if(fullWin == false) {
    				buffer = queue.front();
    				SendFRAMEPacket((unsigned char *)buffer.pframe, buffer.size);
    				fullWin = true;
    			}
    			break;
    		}
    		case MSG_TYPE_RECEIVE: {	//系统接收到一个帧的ACK
    			buffer = queue.front();
    			unsigned int ack;
    			ack = ntohl(((frame *)pBuffer)->ack);
    			if(ntohl(buffer.pframe->seq) == ack) {	//发送序列号等于返回确认号
    				queue.pop_front();
    				buffer = queue.front();
    				fullWin = false;
    				if(queue.empty() == false)	//发送队列存在等待发送的帧
    					SendFRAMEPacket((unsigned char *)buffer.pframe, buffer.size);
    					fullWin = true;
    			}
    			break;
    		}
    		default: 
    			return -1;
    			break;
    	}
    	return 0;
    }
    
    /*
    * 回退n帧测试函数
    */
    int stud_slide_window_back_n_frame(char *pBuffer, int bufferSize, UINT8 messageType)
    {
    	Buffer buffer;
    	switch(messageType) {
    		case MSG_TYPE_TIMEOUT: {	//某个帧超时
    			printf("timeout\n");
    			int serial;
    			serial = ntohl(*(unsigned int *)pBuffer);
    			int i;
    			for(i=0; i<WINDOW_SIZE_BACK_N_FRAME && i<queue.size(); i++) {
    				buffer = queue[i];
    				SendFRAMEPacket((unsigned char *)buffer.pframe, buffer.size);
    			}
    			break;
    		}
    		case MSG_TYPE_SEND: {	//系统要发送一个帧
    			buffer.pframe = new frame;
    			*buffer.pframe = *(frame *)pBuffer;
    			buffer.size = bufferSize;
    			queue.push_back(buffer);	//放入队尾
    			//int i;
    			//for(i=back_n_frame_win; i<WINDOW_SIZE_BACK_N_FRAME && i<queue.size(); i++) {
    			buffer = queue[back_n_frame_win];
    			if(back_n_frame_win < WINDOW_SIZE_BACK_N_FRAME && back_n_frame_win < queue.size()) {	
    				SendFRAMEPacket((unsigned char *)buffer.pframe, buffer.size);
    				back_n_frame_win ++;
    			}
    			break;
    		}
    		case MSG_TYPE_RECEIVE: {	//系统接收到一个帧的ACK
    			int i, j;
    			unsigned int ack;
    			ack = ntohl(((frame *)pBuffer)->ack);
    			for(i=0; i<WINDOW_SIZE_BACK_N_FRAME && i<queue.size(); i++) {
    				buffer = queue[i];
    				if(ntohl(buffer.pframe->seq) == ack) {	//发送序列号等于返回确认号
    					for(j=0; j<=i; j++) {
    						queue.pop_front();
    						back_n_frame_win--;
    						//printf("back_n_frame_win is %d\n", back_n_frame_win);
    					}
    					for(j=back_n_frame_win; j<WINDOW_SIZE_BACK_N_FRAME && j<queue.size(); j++) {
    						buffer = queue[j];
    						SendFRAMEPacket((unsigned char *)buffer.pframe, buffer.size);
    						back_n_frame_win++;
    					}
    					break;
    				}
    			}
    			break;
    		}
    		default: 
    			return -1;
    			break;
    	}
    	return 0;
    }
    
    /*
    * 选择性重传测试函数
    */
    int stud_slide_window_choice_frame_resend(char *pBuffer, int bufferSize, UINT8 messageType)
    {
    	Buffer buffer;
    	switch(messageType) {
    		case MSG_TYPE_TIMEOUT: {	//某个帧超时
    			printf("timeout\n");
    			int serial;
    			serial = ntohl(*(unsigned int *)pBuffer);
    			int i;
    			for(i=0; i<WINDOW_SIZE_BACK_N_FRAME && i<queue.size(); i++) {
    				buffer = queue[i];
    				//if(serial == ntohl(buffer.pframe->seq))
    				SendFRAMEPacket((unsigned char *)buffer.pframe, buffer.size);
    			}
    			break;
    		}
    		case MSG_TYPE_SEND: {	//系统要发送一个帧
    			buffer.pframe = new frame;
    			*buffer.pframe = *(frame *)pBuffer;
    			buffer.size = bufferSize;
    			queue.push_back(buffer);	//放入队尾
    			//int i;
    			//for(i=choice_frame_resend_win; i<WINDOW_SIZE_BACK_N_FRAME && i<queue.size(); i++) {	
    			buffer = queue[choice_frame_resend_win];
    			if(choice_frame_resend_win < WINDOW_SIZE_BACK_N_FRAME && choice_frame_resend_win < queue.size()) {	
    				SendFRAMEPacket((unsigned char *)buffer.pframe, buffer.size);
    				choice_frame_resend_win ++;
    			}			
    			break;
    		}
    		case MSG_TYPE_RECEIVE: {	//系统接收到一个帧的ACK
    			int i, j;
    			unsigned int ack;
    			ack = ntohl(((frame *)pBuffer)->ack);
    			if(ntohl(((frame *)pBuffer)->kind) == nak) {
    				printf("nak\n");
    				for(i=0; i<WINDOW_SIZE_BACK_N_FRAME && i<queue.size(); i++) {
    					buffer = queue[i];
    					if(ntohl(buffer.pframe->seq) == ack) {
    						SendFRAMEPacket((unsigned char *)buffer.pframe, buffer.size);
    						break;
    					}
    				}
    			}
    			else {	
    				for(i=0; i<WINDOW_SIZE_BACK_N_FRAME && i<queue.size(); i++) {
    					buffer = queue[i];
    					if(ntohl(buffer.pframe->seq) == ack) {	//发送序列号等于返回确认号
    						for(j=0; j<=i; j++) {
    							queue.pop_front();
    							choice_frame_resend_win--;
    						}
    						for(j=choice_frame_resend_win; j<WINDOW_SIZE_BACK_N_FRAME && j<queue.size(); j++) {
    							buffer = queue[j];
    							SendFRAMEPacket((unsigned char *)buffer.pframe, buffer.size);
    							choice_frame_resend_win++;
    						}
    						break;
    					}
    				}
    			}
    			break;
    		}
    		default: 
    			//return -1;
    			break;
    	}
    	return 0;
    }
    


    展开全文
  • 滑动窗口实验报告,主要是针对goBackN和选择重传等滑动窗口协议的实现。
  • 滑动窗口协议  一图胜千言,看下面的图,简单解释下:  发送和接受方都会维护一个数据帧的序列,这个序列被称作窗口。发送方的窗口大小由接受方确定,目的在于控制发送速度,以免接受方的缓存不够大,而导致溢出,...

    本文转自 http://www.cnblogs.com/ulihj/archive/2011/01/06/1927613.html

    滑动窗口协议

        一图胜千言,看下面的图,简单解释下:

        发送和接受方都会维护一个数据帧的序列,这个序列被称作窗口。发送方的窗口大小由接受方确定,目的在于控制发送速度,以免接受方的缓存不够大,而导致溢出,同时控制流量也可以避免网络拥塞。下面图中的4,5,6号数据帧已经被发送出去,但是未收到关联的ACK,7,8,9帧则是等待发送。可以看出发送端的窗口大小为6,这是由接受端告知的(事实上必须考虑拥塞窗口cwnd,这里暂且考虑cwnd>rwnd)。此时如果发送端收到4号ACK,则窗口的左边缘向右收缩,窗口的右边缘则向右扩展,此时窗口就向前“滑动了”,即数据帧10也可以被发送。


        下面就滑动窗口协议做出更详细的说明,这里为了简单起见设定发送方窗口大小为2,接受方大小为1。看下面图:


      1:初始态,发送方没有帧发出,发送窗口前后沿相重合。接收方0号窗口打开,等待接收0号帧; 
      2:发送方打开0号窗口,表示已发出0帧但尚确认返回信息。 此时接收窗口状态不变; 
      3:发送方打开0、1号窗口,表示0、1号帧均在等待确认之列。至此,发送方打开的窗口数已达规定限度,在未收到新的确认返回帧之 前,发送方将暂停发送新的数据帧。接收窗口此时状态仍未变; 
      4:接收方已收到0号帧,0号窗口关闭,1号窗口打开,表示准备接收1号帧。此时发送窗口状态不 变; 
      5:发送方收到接收方发来的0号帧确认返回信息,关闭0号窗口,表示从重发表中删除0号帧。此时接收窗口状态仍不变 
      6:发送方继续发送2号帧,2号窗口 打开,表示2号帧也纳入待确认之列。至此,发送方打开的窗口又已达规定限度,在未收到新的确认返回帧之前,发送方将暂停发送新的数据帧,此时接收窗口状态 仍不变; 
      7:接收方已收到1号帧,1号窗口关闭,2号窗口打开,表示准备接收2号帧。此时发送窗口状态不变; 
      8:发送方收到接收方发来的1号帧收毕的确认信 息,关闭1号窗口,表示从重发表中删除1号帧。此时接收窗口状态仍不变。

    1比特滑动窗口协议

        上面说的只是滑动窗口协议的理论,实际应用中又有不同。首先就是停等协议(stop-and-wait),这时接受方的窗口和发送方的窗口大小都是1,1个比特就够表示了,所以也叫1比特滑动窗口协议。发送方这时自然发送每次只能发送一个,并且必须等待这个数据包的ACK,才能发送下一个。虽然在效率上比较低,带宽利用率明显较低,不过在网络环境较差,或是带宽本身很低的情况下,还是适用的。看下面的流程图:

    后退n协议

        停等协议虽然实现简单,也能较好的适用恶劣的网络环境,但是显然效率太低。所以有了后退n协议,这也是滑动窗口协议真正的用处,这里发送的窗口大小为n,接受方的窗口仍然为1。具体看下面的图,这里假设n=9: 
        首先发送方一口气发送10个数据帧,前面两个帧正确返回了,数据帧2出现了错误,这时发送方被迫重新发送2-8这7个帧,接受方也必须丢弃之前接受的3-8这几个帧。 
        后退n协议的好处无疑是提高了效率,但是一旦网络情况糟糕,则会导致大量数据重发,反而不如上面的停等协议,实际上这是很常见的,具体可以参考TCP拥塞控制。

    选择重传协议

        后退n协议的另外一个问题是,当有错误帧出现后,总是要重发该帧之后的所有帧,毫无疑问在网络不是很好的情况下会进一步恶化网络状况。

        重传协议便是用来解决这个问题。原理也很简单,接收端总会缓存所有收到的帧,当某个帧出现错误时,只会要求重传这一个帧,只有当某个序号后的所有帧都正确收到后,才会一起提交给高层应用。重传协议的缺点在于接受端需要更多的缓存



    展开全文
  • (1)向网络层提供一个定义良好的服务接口 (2)处理传输错误------差错控制 (3)调节数据流,确保慢速的接收方不会被快速的发送方淹没----- 流量控制 (一) 提供给网络层的服务 一般情况下,数据链路层会提供...

    1、数据链路层解决的问题

    数据链路层在接受到物理层的比特流进行成帧,数据链路层使用物理层提供的服务在通信信道上发送和接收比特,它主要完成以下功能:
    (1)向网络层提供一个定义良好的服务接口
    (2)处理传输错误------差错控制
    (3)调节数据流,确保慢速的接收方不会被快速的发送方淹没----- 流量控制

    (一) 提供给网络层的服务

    一般情况下,数据链路层会提供一下三种可能的服务
    (1)无确认的无连接服务----以太网
    (2)有确认的无连接服务----WiFi
    (3)有确认的有连接服务----电话

    (二)差错检测和纠正

    1、纠错码:
    (1)海明码,
    (2)二进制卷积码
    (3)里德所罗门码
    (4)低密度奇偶效验码
    纠错码一般应用在无线链路中,但一般这种传输链路很少出现错误,所以对于偶尔出现的错误采用差错检测和重传的处理方式更加有效。
    2、检错码
    (1)奇偶
    (2)校验和
    (3)循环冗余校验(CRC)

    (三)数据链路层的协议

    在数据链路层具有很多的协议
    1、基于单工的:乌托邦式、无错信道上的单工停-等式协议、有错信道上的单工停-等式协议
    2、全双工的:滑动窗口协议

    滑动窗口协议

    为了完成流量控制,TCP使用滑动窗口协议,使用这种方法的时候,发送方和接收方向外通信各使用一个窗口。这个窗口覆盖了缓存的一部分,在缓存中的字节是从应用进程传送来的,在这个窗口中的字节就是可以发送而不必考虑确认的。这个想象的窗口有两个边沿:一个在左,一个在右。这个窗口叫做滑动窗口,因为左沿和右沿都可以滑动。

    在这里插入图片描述
    SWS(发送窗口大小):Send Window Size
    LAR(最近接收到的确认帧):Last Acknowledegement Received
    LFS(最近发送的帧):Last Frame Sent
     窗口大小取决于下面两个数中的较小值:接收窗口(rwnd)和拥塞窗口(cwnd)。接收窗口是由对方发送的包含确认的报文段中所给出的值,这是另一端在缓存溢出和数据被丢失之前所能接受的字节数。拥塞窗口是由网络为避免拥塞而确定的值。

    右沿窗口向右移动表示展开窗口,说明允许从缓存中发送更多新的字节;

    左沿窗口向右移动表示合拢窗口,说明某些字节已经被确认了,发送端不必再担心它们。https://www.cnblogs.com/Mr24/p/6576197.html

    1、1位滑动窗口协议
    2、回退N协议
    3、选择重传协议

    基于选择重传的滑动窗口协议这里有一个实验和代码模拟:
    https://download.csdn.net/download/tangyuan_sibal/10812260

    展开全文
  • Go Back N 协议 ( 暂无 ack ) #include &lt;iostream&gt; #include &lt;cstring&gt; #include "protocol.h" #include "datalink.h" using namespace std ; #pragma ...

    环境 : Microsoft Visual C++ 6.0

    Go Back N 协议  ( 暂无 ack )

    #include <iostream>
    #include <cstring>
    #include "protocol.h"
    #include "datalink.h"
    using namespace std  ;
    #pragma comment ( lib  , "Protocol.lib" )
    #define rep ( i , j , n ) for ( int i = int(j) ; i < int(n) ; ++i )
    // 帧类型
    #define FRAME_DATA 1
    #define FRAME_ACK  2
    // 窗口大小定义
    #define MAX_SEQ 7  
    // 超时规定   
    #define DATA_TIMER  1200        
    // 帧数据结构定义
    
    namespace YHL {
        class FRAME { 
        public:
            unsigned char type ;            // 种类
            unsigned char ack ;             // ack
            unsigned char seq ;             // 序号
            unsigned char data[PKT_LEN] ;   // 数据部分
            unsigned int  padding ; 
        public:
            FRAME () {}
            FRAME ( unsigned char _seq , unsigned char _ack )
                : seq ( _seq )
                , ack ( _ack ) 
            {}
        } One ;    // One 作为反复使用的帧
        
        // 事件参数
        int arg = 0 ;   
        // 物理层是否准备好了              
        int phl_ready = 0 ; 
        // 发送方的左边界    
        unsigned char ack_expected = 0 ;
        // 发送方的右边界 + 1
        unsigned char next_frame_to_send = 0 ;
        // 接受窗口正在移动的号
        unsigned char frame_expected = 0 ;
        // 发送窗口的数量
        unsigned char nbuffered = 0 ;
        // 存储发送窗口
        unsigned char out_buf[MAX_SEQ+1][PKT_LEN] ;
        // 令窗口滑动, 不超过 MAX_SEQ
        unsigned char Go_On ( unsigned char &NO ) {
            return NO = ( NO + 1 ) % ( MAX_SEQ + 1 ) ;
        }        
        // 判断 ack 是否落在发送窗口内
        int between ( unsigned char a , unsigned char b , unsigned char c ) {
            return ( ( ( a <= b ) && ( b < c ) ) 
                  || ( ( c < a ) && ( a <= b ) ) 
                  || ( ( b < c ) && ( c < a ) ) ) ;
        }
        // 添加 CRC 校验, 发往物理层
        void Add_Crc_and_Send ( unsigned char *frame , int len ) { 
            *(unsigned int *)( frame + len ) = crc32 ( frame , len ) ;
            send_frame ( frame , len + 4 ) ;
            phl_ready = 0 ;
        }
        // 发送一个数据帧
        void Send_Data_Frame ( unsigned char frame_nr , 
                    unsigned char frame_expected ) {
            int ack = ( frame_expected + MAX_SEQ ) % ( MAX_SEQ + 1 ) ;
            FRAME One ( frame_nr , ack ) ;
            // 根据序号 frame_nr 获取要发送的数据字段
            memcpy ( One.data , out_buf[frame_nr] , PKT_LEN ) ;
            dbg_frame ( "Send DATA %d %d , ID %d\n" , One.seq , One.ack , *(short *)One.data ) ;
            Add_Crc_and_Send ( (unsigned char *)&One , 3 + PKT_LEN ) ; 
            start_timer ( frame_nr % MAX_SEQ , DATA_TIMER ) ;             
        }
        // 网络层准备好了
        void Net_Layer_OK ()  {
            ++nbuffered ;                
            get_packet ( out_buf[next_frame_to_send] ) ;
            Send_Data_Frame ( next_frame_to_send , frame_expected ) ;
            Go_On ( next_frame_to_send ) ;
        }
        // 收到一个数据帧
        void Recieve_Data () {
            // 先进行校验
            int len = recv_frame ( (unsigned char *)&One , sizeof One ) ;
            if ( len < 5 || crc32 ( (unsigned char *)&One , len ) ) {
                dbg_frame ( "Recv Bad packet\n" ) ;
                return ;
            }
            // 如果的确是接收窗口当前的序号
            if ( One.seq == frame_expected ) {
                dbg_frame ( "Recv DATA %d %d , ID %d\n" , One.seq , One.ack , *(short *)One.data ) ; 
                put_packet ( One.data , len - 7 ) ; // 减去七位非数据位
                Go_On ( frame_expected ) ;
            }
            // 看是否可以腾出发送窗口的左边
            while ( between ( ack_expected , One.ack , next_frame_to_send ) ) {
                --nbuffered ;
                stop_timer ( ack_expected ) ;
                Go_On ( ack_expected ) ;
            }
        }
        // 协议初始化
        void Init_Protocol ( int argc , char **argv ) {
            protocol_init ( argc , argv ) ;
            lprintf ( "Fluence_YHL : " __DATE__"  "__TIME__"\n" ) ;
            disable_network_layer () ;
        }
        // 如果 ack 超时了
        void Data_Time_Out () {
            dbg_event ( "---- DATA %d timeout\n" , arg ) ;  
            // 要传的下一帧是 ack_expected, 也就是左边界     
            next_frame_to_send = ack_expected ;
            for ( int i = 1 ; i <= nbuffered ; ++i ) {
                Send_Data_Frame ( next_frame_to_send , frame_expected ) ;
                Go_On ( next_frame_to_send ) ;
            }
        }
        // 控制网络层开闭
        void Enable_Net_Layer () {
            if ( nbuffered < MAX_SEQ && phl_ready )
                enable_network_layer() ;
            else
                disable_network_layer() ;
        }
        // 获取一个事件
        int Get_event () {
            return wait_for_event ( &arg ) ;
        }
    }
    
    int main ( int argc , char **argv ) {
        YHL::Init_Protocol ( argc , argv ) ;
        while ( true ) {
            // 获取一个事件
            int event = YHL::Get_event () ;
            switch ( event ) {
                // 网络层准备好了
                case NETWORK_LAYER_READY : YHL::Net_Layer_OK () ; break ;
                // 收到一个帧
                case FRAME_RECEIVED : YHL::Recieve_Data () ; break ;
                // 物理层准备好了
                case PHYSICAL_LAYER_READY : YHL::phl_ready = 1 ; break ;
                // 超时重发
                case DATA_TIMEOUT : YHL::Data_Time_Out () ; break ;
                // 
                default : break ;
            }
            // 控制网络层流量
            YHL::Enable_Net_Layer () ;
        }
        return 0 ;
    }

     

    展开全文
  • 续上次 计算机网络实验 Go Back N (不含 ACK)滑动窗口协议 C++ 今天实现了 带有 ACK 的 环境 :Microsoft Visual C++ 6.0 代码实现: #include &amp;amp;amp;lt;iostream&amp;amp;amp;gt; #...
  • 自己编的程序,利用所学数据链路层原理,自己设计一个滑动窗口协议并在仿真环境下编程实现有噪音信道环境下两站点之间无差错双工通信。信道模型为8000bps 全双工卫星信道,信道传播时延270 毫秒,信道误码率为10-5,...
  • 有很详细的代码,运行结果和解释,很好懂的,给大家共享啦
  • 1. 首先我们知道TCP可靠传输原理中的停止等待协议,该协议就是发送方每发送一个报文段后就停止发送,一直等收到接收方的确认后再发送下一个报文段。  TCP通信是全双工的,我们假定A为发送方,B为接收方。A会有一...
  • 实验文件夹的Example文件夹下有三个样例程序:gobackn.exe,selective.exe,stopwait.exe; 可通过命令行窗口打开:在文件夹上方的地址栏里直接输入cmd即可打开命令行窗口(如图); 打开两个命令行窗口; ...
  • 1、滑动窗口协议 引言 在之前的协议中,数据帧只在一个方向上传输。而在大多数实际环境中,往往需要在两个方向上同时传输数据,一种做法是使用一条链路来实现双向传输,从机器A到机器B的数据帧可以与从机器A到...
  • 在下最近在完成计算机网络的课程设计,题目要用socket实现滑动窗口协议,但是有点要求看不懂,百度也找不到,想请教一下: **超时计数器技术**, **帧编号盘重技术**, **校验和技术**, 使用**重传技术**是否...
  • 滑动窗口协议

    2012-05-11 20:33:39
    允许我们这样做的算法称为滑动窗口( sliding window),时间线如图2 - 2 1所示。 1. 滑动窗口算法 滑动窗口算法工作过程如下。首先,发送方为每1帧赋一个序号(sequence number),记作S e q N u m。现在...
  • 滑动窗口协议(Sliding Window Protocol),属于TCP协议的一种应用,用于网络数据传输时的流量控制,以避免拥塞的发生。该协议允许发送方在停止并等待确认前发送多个数据分组。由于发送方不必每发一个分组就停下来...
  • 本章围绕TCP协议滑动窗口这一主题展开讨论,第一部分主要是对TCP滑动窗口的基础工作原理进行阐述,第二部分会深入的理解滑动窗口,第三部分会根据一个工作中实际遇到的例子来理解滑动窗口。 背景介绍 TCP...
  • 从传输数据来讲,TCP/UDP以及其他协议都可以完成数据的传输,从一端传输到另外一端,TCP比较出众的一点就是提供一个可靠的,流控的数据传输,所以实现起来要比其他协议复杂的多,先来看下这两个修饰词的意义: ...
1 2 3 4 5 ... 20
收藏数 1,536
精华内容 614