阶段目标
本阶段需要对实体进行编址,实现NET层的IP地址到MAC层地址的映射,解决各层实体的标定区分和数据投递,同时网络层记录路由表,并且可以按照路由进行转发,实现端到端的信息交换。
本阶段是整个项目的最终阶段,也就是将整个项目进行整合,要体现在一定的拓扑上利用层次化的模型,模拟真实的虚、实通信情况。
设计描述
1、实体编址
报文格式
IP报文格式采用字节(字符)形式设计,网络层数据包格式如下:
目的IP | 源IP | 数据字节 | 下一跳IP |
---|---|---|---|
3 | 1 | ABCD | 2 |
由于所有例程中网元均的网络层均只有一个,因此不方便像现实路由器对每个端口分别分配IP,此处仅以与设备号相同的U8类型的一字节整数作为设备的IP。同时由于之前LNK层设计过程中对各端口查找是通过find_port()查找MAC表映射得到端口号,因此此处为了简化,也不再需要重新对每个端口分配单独的MAC地址,一个设备共用一个跟设备号数值相同的3bit二进制MAC地址。而区别每个端口通过find_port()函数实现,实现简化设计。(大概相当于不同的端口号替代了现实意义的端口具有不同的MAC地址)
因此,NET层和LNK层实体之间的映射通过NET层的int find_port(U8 IP,U8* nextIP)得到对应低层的端口号以及下一跳的IP地址来实现,免去了ARP协议此类的设计。
此处,由于受限于已有的参考例程和例程中完整的接口体系,不增加接口传递参数的复杂程度,。为简化设计,LNK层从NET层获取下一跳IP地址然后映射为MAC地址,通过查看报文最后一位U8类型的字符数据来得到目的MAC地址,虽然在一定程度上不服从层次模型中低层不能查看高层的内容的规定,但在不增加层间接口传输的除data和len数据以外的参数的情况下,保证不会改变源、目的IP地址的情况下,大大简化了设计。
(
现在回顾起来觉得有人可能会问,为什么数据包格式要加入下一跳的IP,直接通过先后查路由表和查MAC表不就好了吗,这样根据目的MAC地址知道下一跳的出口了?
但是事实情况由于课程组给的拓扑中,路由器均是一个高层NET层,多个独立的一个低层LNK层和一个PHY层绑定,这样根本无法和之前LNK层设计的交换机部分一个LNK层和多个PHY层绑定形成统一;并且即使对路由器的LNK层均有MAC表,也会因为LNK层转发的进出口不同导致不同LNK层学到的MAC表不一样。这样也不方便上下层之间交互设计。
所以这个地方的下一跳IP其实也包含有记录下一个MAC地址的意味。
所以这只是我当时能想到的比较好的解决办法,虽然看起来并不是那么规范。
)
具体映射是下一跳单字节IP(U8类型)地址同数值映射为3bit二进制的MAC地址(U8类型),映射函数为LNK层的IP_to_mac()。
同时,从APP层输入的字符中,最后一个字节表示数据希望传到的目的设备号。便于从用户层面传达希望到达的目的IP。(后面向下传递到NET层,不会将这一位作为数据参与接下来的传输)
涉及函数:
int add_IP(U8* s, int len, U8 dstIP, U8* S);//添加源、目的IP地址
int find_port(U8 dstIP, U8* nexthop);//查找出口端口号以及下一跳IP
2、路由表设计
Router Table
DstIP | Nexthop | Port | Metric |
---|---|---|---|
3 | 1 | 0 | 1 |
路由表结构体
struct Router_table {
U8 destination; //目的地“IP”,1个字节
U8 nexthop; //下一跳IP
int port; //出口端口号
int metric; //度量
};
本阶段设计主要用到的是除Metric以外前三个参数,因为是采用人工配置静态路由的方式,Metric没有被利用起来,仅在此表示路由表有设计度量这一表项。
//全局路由表声明
struct Router_table* rt;
int table_len;
--------路由表函数---------
void print_router_table();//打印全局路由表
void init_router_table();//初始化路由表
3、路由配置
在本阶段的路由配置过程中,采用静态路由的方式,在初始化时进行人工配置。起初设计时仅针对NET层低层LNK实体超过1个的进行初始化配置静态路由表,但后来发现在单独PC的NET层进行向下传输存在MAC地址映射问题问题。故对单独的PC在程序中也有“路由表”的配置,但此处“路由表”的实质其实是配置默认网关的IP,即下一跳的IP。
4、路由器的存储转发
路由器跟交换机类似,都具有存储转发的功能,因此也可以参考交换机的Timeout()中的循环队列进行定时转发。(可以回顾阶段三的设计)
struct queue_t {
int front;
int rear;
U8* data[MAX_QUE];
int len[MAX_QUE];
int next_hop[MAX_QUE];
int src_hop[MAX_QUE];
};
5、端到端的图片传输
在本阶段实现端到端图片传输时,采用base64的编码方式对图片编码为string,然后再转换为U8类型的字节数据进行发送,并在接收端进行解码输出。
测试情况
1、六个网元混合组网拓扑按路由表转发测试情况
设备2的路由表配置
设备5向设备4发送数据
设备5
设备4
可以看到,设备4APP层正确的接收到了设备5发送来的字符串,证明按路由表转发正常,可以实现端到端正常的信息交换;需要特别说明的是,由于设备2LNK层的实体0连续两次接收应答帧校验错误,故进行了2次重传,导致设备4APP层多接收到了两次重复字符串。(当时测试时忽略了误码率的问题,如果设置误码率为0,应该只会接收到一次数据)
2、图片发送情况(设备6向设备3发送图片)
发送图片test.jpg并编码
解码接收图片out.jpg
其他想说的话
本阶段主要实现了在静态路由下,路由器的寻径转发功能,完成了端到端的信息交流,以及利用base64编码进行图片传输。
本阶段的难点感觉并不在NET层的代码编写上面,而是在各层自定义代码的协同上面,在使各层代码相互协调合作的情况上,让我花费了很多的时间和精力。
同时由于网络层的路由表没有完成动态路由的原因,导致路由表中度量(Metric)一参数并没有被很好的利用起来,没有体现选路的最优化原则。并且静态路由在使用和健壮性上也不是很好,也反映出现实中大多路由器采用动态路由的应用意义。
再者,反思前面的阶段,由于链路层采用4位CRC校验和停等协议进行差错控制,以及没有采用分片传输的原因,导致本阶段进行混合组网端到端传输的效率明显不够,特别是图片传输时,在物理模拟软件还设置有一定误码率的时候,仅能传输像素较小的图片(测试用例好像像素仅10×14),像素稍微过大,即数据量过大,就会导致中间的某一环传输出现问题,不停的需要重传,几乎无法正确传输过去。这也是为什么我后来反思如果设置能够设计分片功能,整个通信传输会更加的准确高效。(当然没有误码率肯定能够传输较大的图片)
整个项目已经完结,回顾下来,觉得还有许多不足和可以改进的地方。也发现对许多功能的设计有了新的想法,或者是原来没想出来的问题有了一些想法。回顾整个过程的时候,脑海里回想起了这一学期一个人抗压,一个人解决大大小小的问题,一次次克服畏难情绪,最终把项目一点一点自己推进的情景。虽然比起周围同学确实很累,但是给我最大的体会就是,曾经我认为再难克服的困难,也会在咬牙中一点一点地被克服,在压力中前进,才应该是人生的一种积极乐观的常态;这不仅是这学期的计通网带给我的,也包括此期间发生的许多其他种种事情。