计算机网络项目——最小网元设计(阶段三)
Posted Couldhelp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算机网络项目——最小网元设计(阶段三)相关的知识,希望对你有一定的参考价值。
阶段目标
本阶段主要完成链路层交换机的功能实现:单播目的地址判收、支持广播、有端口地址表、反向地址学习,并在含交换机的多网元情况下完成测试。
设计描述
1、帧结构
原理:
在阶段二基础的帧格式下,在帧头增加了源、目的MAC地址,其中MAC地址采用3位二进制码编码(考虑到配置文件设备数不超过8,也算是视情况偷了个懒),并且对本来为6字节的MAC地址简单处理,设备号即为MAC地址(如设备号为3则MAC地址为(int)3,然后转换为二进制目的和源MAC地址),帧格式其他部分同阶段二。
此处的源MAC地址可以直接利用配置文件所读取到的参数直接赋值,而目的MAC地址则需要手动进行输入,或在APP层进行键入传递参数,或直接利用LNK层封装时进行手动键盘键入。
01111110 | 010 | 001 | 0 | 0000 | 0011001… | 0110 | 01111110 |
---|---|---|---|---|---|---|---|
帧头定界符 | Dst_MAC | Src_MAC | ACK标志位 | 帧序号 | 数据位 | CRC校验位 | 帧尾定界符 |
涉及函数:
发送方:
int add_src_mac(U8* s, int len); //插入源MAC地址
int add_dst_mac(U8* s, int len, int dst_mac); //插入目的MAC地址
接收方:
int get_src_mac(U8* s); //提取源MAC地址
int get_dst_mac(U8* s); //提取目的MAC地址
要点:
- 需要注意int类型的MAC地址到U8类型的二进制MAC数据的转换。
- 为了避免在头部添加数据所带来的头部指针移动的麻烦,因此一种较好的办法就是预先分配足够的内存再往其中填数据,有点类似一些协议中帧长固定。
2、按目的转发
原理:
本例中,通过lowerNumber的数量是否大于1来初步判断是否为交换机,以区别交换机和一般主机的LNK层。
每接收一帧,查看其目的MAC地址,判断是否为自身,如果是自身则向上层递交,不是则按端口号转发,若无此地址则广播到其他端口。其中转发和广播均通过Timeout()函数定时发送实现,以达到交换机先“存储”再转发的效果。
对于交换机,在所收到的数据需要进行转发时,需要进行拆包,与自己的MAC地址表进行比对,然后利用find_port()函数找到MAC表映射的转发端口,进行转发。
涉及函数:
-----全局MAC地址表-----
struct Switch_table
int addr;
int port;
;//建立交换机MAC表
//全局MAC表,个数最大为PORTNUM
struct Switch_table* st;
int table_len; //MAC表端口数
-----查表和表输出函数-----
void print_switch_table(); //打印MAC表
int find_port(int addr); //根据目的地址查询并返回端口号
----Timeout()函数中存储转发所需要的循环队列----
#define MAX_QUE 60
struct queue_t
int front;
int rear;
U8* data[MAX_QUE]; //数据数组
int len[MAX_QUE]; //每个帧的帧长数组
int next_port[MAX_QUE]; //待转发数据的转发出口
int src_port[MAX_QUE]; //接收数据的源端口,记录下来便于后面的反向地址学习以及未知广播
;
//全局循环队列缓冲区
struct queue_t my_buffer_pool;
int enqueue(struct queue_t* que, U8* buf, int len, int nextPort, int srcPort);
U8* dequeue(struct queue_t* que, int* len, int* nextPort, int* srcPort);
void initqueue(struct queue_t* que); //初始化队列,建议在程序开始运行时的initialFunction()中使用
void freequeue(struct queue_t* que); //清空缓存区
要点:
- 其实没有过多需要注意的地方,弄清楚MAC地址和端口的映射,然后再转发时存下相关数据,并且注意参数的传递就好了。
- 对于全局缓冲区struct queue_t my_buffer_pool,由于他不是一个指针,所以在函数参数传递中,一定是传入的它的地址,就是要取地址传入&my_buffer_pool。
3、反向地址学习
原理:
接收帧后,获取其源MAC地址,如果MAC表中没有此映射,则进行该地址的学习,或者在原有基础上进行更新(一定是要更新的,但这也可能在广播风暴中造成地址漂移)。
本例设计时考虑到模型的规模较小,设备不多,如果采用先进行ARP进行定时广播学习,会浪费信道资源(其实一想也不会太浪费),故采用按需学习的方式。本例中反向学习re_addr_learn()仅对接收帧内的源地址进行学习,对于没有参与通信设备的地址不进行学习。
涉及代码:
void re_addr_learn(int addr, int port); //反向地址学习
4、未知广播
交换机对于未知的目的地址将会广播到其他所有端口,在Timeout()函数中利用for循环依次向除接收端口转发出去即可。但在交换机组成环型拓扑时会引起广播风暴占用大量带宽。故应设计生成树算法进行此问题的规避。(但由于一个人的人力问题和时间问题,生成树没有想到怎么较好的实现,可能是图论算法没有学的很好的缘故,DFS和BFS没有想好咋在这个里面简单使用)
测试情况
1、含交换机的一条线拓扑测试情况
发送一次数据,由于数据帧和确认帧的来回两次转发,交换机产生两次MAC地址的反向学习,并打印显示MAC表。
2、广播风暴所带来的MAC表漂移情况
在以上无生成树环型拓扑图中,理论分析,各交换机会不断进行转发,形成广播风暴,并伴随MAC表漂移。以下测试从网元1的APP层发送一次数据,观测各交换机LNK层情况。
可以观测到,在网元1只发送了一次数据的情况下,其他各个网元LNK层均同上图一样,不停的转发广播数据帧,不停的更新MAC表,并且同一地址的映射发生漂移,同理论分析一样形成广播风暴。
其他想说的话
虽然交换机的基本功能都已经实现,但是无奈一个人的工作量还是太大,生成树当时没能来得及实现,也是有点小遗憾。而且在这个阶段由于没有牵涉进来网络层的路由,所以不太好指明数据最终的目的设备是谁,当时是简单通过代码里面手动修改。但在引入路由和IP与MAC的映射关系之后,这个问题便没有了。
这个阶段个人认为除了完整的生成树协议的实现,其他功能的实现还是比较简单的,而且仍然主要停留在数据链路层,还没有考虑与其他层交互的设计,导致难度也没有那么高。
以上是关于计算机网络项目——最小网元设计(阶段三)的主要内容,如果未能解决你的问题,请参考以下文章