BUPT 计网实验一:数据链路层滑动窗口协议的设计与实现

一、从样例程序上手

在实验文件夹的Example文件夹下有三个样例程序:gobackn.exe,selective.exe,stopwait.exe;

可通过命令行窗口打开:在文件夹上方的地址栏里直接输入cmd即可打开命令行窗口(如图);

打开两个命令行窗口;

 

打开后任选一个程序名,如stopwait,在两个窗口内分别输入 stopwait A 和 stopwait B,连接建立,程序开始执行;

命令行中输入的指令可带选项,具体可参考实验文档,常用的有如图:

注:datalink -d3 A 可打印出协议运行信息,便于debug;

 

 程序运行正常时会打印出这样的报告:480.484 .... 1784 packets received, 7611 bps, 95.14%, Err 38 (9.9e-006)

其表示含义为:时间坐标为 480.484 秒,收到了 1784 个分组,网络层有效数据传输率 7611bps,实际线路利用率
                         95.14%,接收方向共检出 38 个帧校验和错误,统计计算出实际误码率 9.9x10 ^(-6) 。

实验要求修改文件夹中的datalink.c文件,完成自己的协议设计。

 二、各模块函数的定义

  1.日志函数

             extern void lprintf(char *fmt,...);  //在输出信息之前首先输出当前的时间坐标;

  2.初始化函数

              void protocol_init(int argc, char **argv); //对运行环境初始化;

  3.与网络层模块的接口函数

              void enable_network_layer(void); //允许网络层发送数据分组;
              void disable_network_layer(void); //不允许网络层发送数据分组;
              int get_packet(unsigned char *packet); //将分组拷贝到指针 packet 指定的缓冲区中,函数返回值为分组长度;
              void put_packet(unsigned char *packet, int len); //两个参数:存放收到分组的缓冲区首地址和分组长度;

  4.事件驱动函数

              int wait_for_event(int *arg); //参数 arg 用于获得已发生事件的相关信息;
              #define NETWORK_LAYER_READY 0 //网络层有待发送的分组;
              #define PHYSICAL_LAYER_READY 1 //物理层发送队列的长度低于 50 字节;
              #define FRAME_RECEIVED 2 //物理层收到了一整帧;
              #define DATA_TIMEOUT 3 //定时器超时,参数 arg 中返回发生超时的定时器的编号;
              #define ACK_TIMEOUT 4 //所设置的搭载 ACK 定时器超时;

  5.与物理层模块的接口函数

              void send_frame(unsigned char *frame, int len); //将内存 frame 处长度为 len 的缓冲区块向物理层发送为一帧;
              int recv_frame(unsigned char *buf, int size); //从物理层接收一帧,size 为用于存放接收帧的缓冲区 buf 的空间大小,返                                                                                        回值为收到帧的实际长度;
              int phl_sq_len(void); //返回当前物理层队列的长度;

注:物理层发送队列最多可以保留 64K 字节。

   6.CRC 校验和的产生与验证

              unsigned int crc32(unsigned char *buf, int len); //校验和正确为 0,否则不为 0;

例:*(unsigned int *)(p + 243) = crc32(p, 243);

功能:指针 p 的定义为 char *p 并且 p 指向一个缓冲区,缓冲区内有 243 字节数据,为这 243 字节数据生成 CRC-32 校验和,并且把这 32 比特校验和附在 243 字节之后;

   7.定时器管理

               unsigned int get_ms(void); //获取当前的时间坐标,单位为毫秒;
               void start_timer(unsigned int nr, unsigned int ms); //用于启动一个定时器;两个参数分别为计时器的编号和超时时间值;
               void stop_timer(unsigned int nr); //中止一个定时器。在定时器未超时之前直接对同一个编号的定时器执行
                                                                      start_timer()调用,将按照新的时间设置产生超时事件。
               void start_ack_timer(unsigned int ms);
               void stop_ack_timer(void);

start_ack_timer()与 start_timer()有两点不同:首先,定时器启动时刻为当前时刻;其次,在先前启动的定时器未超时之前重新执行 start_ack_timer()调用,定时器将依然按照先前的时间设置产生超时事件 ACK_TIMEOUT。

  8.协议工作过程的跟踪和调试

              extern void dbg_event(char *fmt, ...); //只有在发生某些不正常事件时才产生输出;
              extern void dbg_frame(char *fmt, ...); //每发送和接收一帧,都打印出相关调试信息便于协议分析;
              extern void dbg_warning(char *fmt, ...);
              char *station_name(void); //获取当前进程所对应的站点名,为字符串”A”或者”B”;

dbg_frame,dbg_event,dbg_warning 三个函数最终调用 lprintf,可以通过命令行参数的--debug 选项或者-d 选项调整输出。

三、程序主体流程

enable_network_layer();
for (;;) {
	event = wait_for_event(&arg);
	switch (event) {
	case EVENT_NETWORK_LAYER_READY:
		len = get_packet(my_buf);
		… …
			break;
	case EVENT_PHYSICAL_LAYER_READY:
		… …
			break;
	case EVENT_FRAME_RECEIVED:
		rbuf_len = recv_frame(rbuf, sizeof rbuf);
		... ...
			break;
	case EVENT_ACK_TIMEOUT:
		... ...
			break;
	case EVENT_DATA_TIMEOUT:
		... ...
			break;
	}
	if (...)
		enable_network_layer();
	else
		disable_network_layer();
}

四、一种实验方案(选择重传协议)

  1.物理层

       为数据链路层提供的服务为8000bps,270ms传播延时,10^(-5)误码率的字节流传输通道。为了仿真实现上述服务质量的信道,利用在同一台计算机上TCP Socket完成两个站点之间的通信。由于同一台计算机上TCP通信传播时延短、传播速度快、没有误码,物理层仿真程序在发送端利用“令牌桶”算法限制发送速率以仿真8000bps线路;在接收端误码插入模块利用一个伪随机数“随机地”篡改从TCP收到的数据,使得所接收到的每个比特出现差错的概率为10^(-5);接收到的数据缓冲后延时270ms才提交给数据链路层程序,以仿真信道的传播时延特性。为了简化程序,省略了成帧功能,数据链路层利用接口函数send_frame()和recv_frame()发送和接收一帧。

  2.数据链路层

       发送方和接收方都维持一个窗口,窗口内部分别包含可发送或已发送但未被确认的可接受的序号。接收到的数据包被缓存起来,当按正确的顺序接收完毕后再提交给网络层。ACK信息通过数据帧捎带确认的方式传递,若遇到长时间无数据帧发送,则产生ACK超时事件(ACK_TIMEOUT),主动发送空的ACK帧。若长时间未收到ACK信息,则产生数据帧超时事件(DATA_TIMEOUT),发送方自动重传未确认帧;当出现帧丢失或校验错误时,接收方会主动发送NAK帧提示发送方立即重传。

       数据链路层通过物理层提供的函数来利用物理层提供的服务。通过get_packet()函数从网络层得到一个分组;当数据链路层成功接收到一个分组后,通过put_packet()函数提交给网络层。

  3.网络层

       利用数据链路层提供的“可靠的分组传输”服务,在站点A与站点B之间交换长度固定为256字节的数据分组。网络层把产生的分组交付数据链路层,并接受数据链路层提交来的数据分组。

        “分组序列发生器”生成的每个分组的前两个字节放置了一个两字节整数作为“分组 ID”,例如:在调试数据帧重传功能时,可以打印出这个分组 ID 以分辨是哪个网络层分组数据在重传。设指针 p 的定义为 unsigned char *p,并且 p 已指向分组的首字节,那么,打印整数*(short *)p 就可以得到分组 ID。站点 A 产生的分组的分组 ID 取值为 10000~19999,站点 B 产生的分组的分组 ID 取值为 20000~29999,分组 ID 值递增,递增到最大值后回卷到最小值。

 

  • 13
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值