概述
广州大学学生实验报告->计算机网络->实验五->网桥+检验和
源代码下载:点击下载
源代码用Java写的,IDE使用idea,如果用idea加载代码无法run,可以点击这里参考
一、 实验目的
复习网桥的工作原理;复习校验和算法。
二、实验环境
Win 10 1903
三、实验内容
1、 写一个程序来模拟网桥功能。
模拟实现网桥的转发功能,以从文件中读取帧模拟网桥从网络中收到一帧,即从两个文件中读入一系列帧,从第一个文件中读入一帧然后从第二个文件中再读入一帧,如此下去。对每一帧,显示网桥是否会转发,及显示转发表内容。
要求:Windows或Linux环境下运行,程序应在单机上运行。
分析:用程序模拟网桥功能,可以假定用两个文件分别代表两个网段上的网络帧数据。而两个文件中的数据应具有帧的特征,即有目的地址,源地址和帧内数据。程序交替读入帧的数据,就相当于网桥从网段中得到帧数据。
对于网桥来说,能否转发帧在于把接收到的帧与网桥中的转发表相比较。判断目的地址后才决定是否转发。由此可见转发的关键在于构造转发表。这里转发表可通过动态生成。
2、 编写一个计算机程序用来计算一个文件的16位效验和。最快速的方法是用一个32位的整数来存放这个和。记住要处理进位(例如,超过16位的那些位),把它们加到效验和中。
要求:1)以命令行形式运行:check_sum infile
其中check_sum为效验程序名,infile为输入数据文件名。
2)输出:数据文件的和
附:效验和(checksum)
参见RFC1071 - Computing the Internet checksum
原理:把要发送的数据看成16比特的二进制整数序列,并计算他们的和。若数据字节长度为奇数,则在数据尾部补一个字节的0以凑成偶数。
例子:16位效验和计算,下图表明一个小的字符串的16位效验和的计算。
为了计算效验和,发送计算机把每对字符当成16位整数处理并计算效验和。如果效验和大于16位,那么把进位一起加到最后的效验和中。
四、 项目设计报告
1. 写一个程序来模拟网桥功能。
1) 程序设计思路
“模拟网桥的转发功能”,这个转发功能是我们的代码要实现的。程序可以以下面的图模拟实际中的网桥。
“从文件中读取帧模拟网桥从网络中收到一帧”,我们可以用文件来虚拟实际中的主机、主机的适配器(适配器向网络发送帧) (这里用虚拟,是因为Linux操作系统中,也是用文件的方式去实现这样的操作的)
用A、B、C、D四个文件夹模拟4台主机
每一台主机中的output子文件模拟主机的适配器的发送
“从第一个文件中读入一帧然后从第二个文件中再读入一帧”可以用程序IO遍历文件夹。
帧的内容可以为:
- 程序类图
- 源代码 (附详细注释)
Bridge类
package com.zhengzeliang;
import java.io.*;
import java.util.Enumeration;
import java.util.Hashtable;
/**
* @author 25894
*/
public class Bridge implements Runnable {
/**
* 网桥的名字,可以参照“主机在网桥中的位置.jpg”,例如b1
*/
private String bridgeName = null;
/**
* 哈希表的第一个参数代表帧的源地址,第二个参数为网桥的接口
*/
private Hashtable forwardingTable = new Hashtable();
Bridge(String bridgeName) {
this.bridgeName = bridgeName;
}
@Override
public void run() {
if ("b1".equals(this.bridgeName)) {
System.out.println("--------------b1网桥------------------");
String[] hostsB1 = {"A", "B", "C", "D"};
// String[] frameSourceAndDestination = new String[2];
//遍历与该网桥相连的所有主机,动态生成转发表
for (String host : hostsB1) {
System.out.println();
System.out.println("主机:" + host);
//获取帧的源地址和目的地址,并动态生成转发表
this.getFrameFileAndDestinationAndSource(host);
}
}
//else下的代码作废,因为实验只要求有一个网桥就行。
// else if ("b2".equals(this.bridgeName)) {
// System.out.println("--------------b2网桥------------------");
// String[] hostsB2 = {"E", "F", "C", "D"};
String[] frameSourceAndDestination = new String[2];
// //遍历与该网桥相连的所有主机
// for (String host : hostsB2) {
// System.out.println();
// System.out.println("主机:" + host);
// //获取帧的源地址和目的地址,并动态生成转发表
// this.getFrameFileAndDestinationAndSource(host);
// }
// }
}
/**
* @author Zzl
* @date 2019/12/1 22:22
* @description 主机内层:找到主机内的待发送的帧的文件夹,对帧进行分析,获取帧中的源地址和目的地址,并将源地址和主机写入转发表
*/
private String[] getFrameFileAndDestinationAndSource(String host) {
//frameSourceAndDestination为长度为2的字符串数组,frameDestinationAndSource[0]为目的地址,frameDestinationAndSource[1]为源地址
String[] frameDestinationAndSource = new String[2];
//进入某一个主机所在的文件夹,并进入发送output文件夹
File hostFile = new File("../" + host + "/output");
//列出output文件夹下的所有帧(等待网桥转发的帧)
File[] frameFiles = hostFile.listFiles();
if (frameFiles.length > 0) {
//缓冲,12个字节(48位的MAC用十六进制写成的是12个字节)的缓冲用于存放MAC源地址或MAC目的地址
byte[] destinationOrSourceBuffer = new byte[12];
String frameDestination;
String frameSourcce;
//对每一个帧进行分析,分析帧的源地址和目的地址
for (File oneFrameFile : frameFiles) {
System.out.println(" 帧名:" + oneFrameFile.getName());
try {
//IO操作
InputStream frameInputStream = new FileInputStream(oneFrameFile);
frameInputStream.read(destinationOrSourceBuffer);
//获取目的MAC地址
frameDestination = new String(destinationOrSourceBuffer);
frameInputStream.read(destinationOrSourceBuffer);
//获取源MAC地址
frameSourcce = new String(destinationOrSourceBuffer);
frameDestinationAndSource[0] = frameDestination;
frameDestinationAndSource[1] = frameSourcce;
frameFiles.clone();
System.out.println(" -->目的地址:" + frameDestinationAndSource[0]);
System.out.println(" -->源地址:" + frameDestinationAndSource[1]);
//将帧的源地址和接口写入转发表
this.writeForwardingTable(host, frameDestinationAndSource[1]);
this.judgeForwardOrNot(frameDestinationAndSource[1], frameDestinationAndSource[0]);
this.listForwardingTable();
} catch (FileNotFoundException e) {
System.out.println("文件没找到");
e.printStackTrace();
} catch (IOException e) {
System.out.println("字节读取失败");
e.printStackTrace();
}
}
}
return frameDestinationAndSource;
}
/**
* @author Zzl
* @date 2019/12/1 22:59
* @description 动态生成转发表:将源地址和接口写入forwardingTable中
*/
private void writeForwardingTable(String host, String frameSource) {
if ("b1".equals(bridgeName)) {
if ("A".equals(host) || "B".equals(host)) {
//写入forwardTable表,第一个参数代表帧的源地址,第二个参数为网桥的接口
forwardingTable.put(frameSource, 1);
} else if ("C".equals(host) || "D".equals(host)) {
forwardingTable.put(frameSource, 2);
}
}
// //else下的代码作废,因为实验只要求有一个网桥就行。
else if ("b2".equals(bridgeName)) {
if ("C".equals(host) || "D".equals(host)) {
//写入forwardTable表,第一个参数代表帧的源地址,第二个参数为网桥的接口
forwardingTable.put(frameSource, 1);
} else if ("E".equals(host) || "F".equals(host)) {
forwardingTable.put(frameSource, 2);
}
}
}
/**
* @author Zzl
* @date 2019/12/1 23:47
* @description 判断网桥是否转发
*/
private void judgeForwardOrNot(String frameSource, String frameDestination) {
if (this.forwardingTable.get(frameDestination) == this.forwardingTable.get(frameSource)
&& this.forwardingTable.get(frameDestination) != null) {
System.out.println(" !!!" + this.bridgeName + "不转发");
} else {
System.out.println(" √√√ " + this.bridgeName + "转发");
}
}
/**
* @author Zzl
* @date 2019/12/2 0:00
* @description 列出转发表的内容
*/
private void listForwardingTable() {
Enumeration listHosts = forwardingTable.keys();
System.out.println(" -------转发表---------");
while (listHosts.hasMoreElements()) {
String listHost = (String) listHosts.nextElement();
System.out.println(" ---" + listHost + "---" + forwardingTable.get(listHost) + "---");
}
System.out.println(" ----------------------");
System.out.println();
}
}
Main类
package com.zhengzeliang;
public class Main {
public static void main(String[] args) {
Bridge b1 = new Bridge("b1");
Bridge b2 = new Bridge("b2");
new Thread(b1,"网桥b1").start();
// new Thread(b2,"网桥b2").start();
}
}
4) 运行结果
(控制台打印出来的东西很长,只截取一部分)
2. 编写一个计算机程序用来计算一个文件的16位效验和。最快速的方法是用一个32位的整数来存放这个和。记住要处理进位(例如,超过16位的那些位),把它们加到效验和中。
- 源代码 (附详细注释)
CheckSumMain类
package com.zhengzeliang;
import java.io.File;
import java.util.Scanner;
/**
* @author 25894
*/
public class CheckSumMain {
public static void main(String[] args){
FileCheckSum fcs = new FileCheckSum();
String checkNum = fcs.fileCheckSum(new File("./checkNum.txt"));
System.out.println("Hello world. ---->" + checkNum);
while (true){
System.out.println("1)以命令行形式运行:check_sum infilen" +
" 其中check_sum为效验程序名,infile为输入数据文件名。n" +
"2)输出:数据文件的和n");
Scanner scanner = new Scanner(System.in);
String consoleInput = scanner.nextLine();
if (consoleInput.startsWith("check_sum")){
String[] consoleInputs = consoleInput.split(" ");
System.out.println(consoleInput);
// for (String input : consoleInputs) {
System.out.println(input);
}
File file = new File(consoleInputs[1]);
checkNum = fcs.fileCheckSum(file);
System.out.println("校验和为:---->" + checkNum);
System.out.println();
}else {
System.out.println("命令输入有误!");
}
}
}
}
CheckSum类
package com.zhengzeliang;
import java.io.*;
class FileCheckSum {
/**
* @author Zzl
* @date 2019/12/1 16:31
* @description 计算文件的16位检验和
*/
String fileCheckSum(File file) {
int checkNum = 0;
try (InputStream inputStream = new FileInputStream(file)) {
byte[] buffer = new byte[2];
int readNum;
while ((readNum = inputStream.read(buffer)) != -1) {
if (readNum == 1) {
//如果读取的字节数是奇数,则在数据尾部补一个字节的0以凑成偶数,可以通过左移8位实现
checkNum = checkNum + ((buffer[0] & 0xFF) << 8);
} else {
//偶数字节数组,每2个字节数组作为一个16位检验和的单位
checkNum = checkNum + ((buffer[0] & 0xFF) << 8 | buffer[1] & 0xFF);
}
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件名输入有误!");
}
//todo 如果文件很大很大,校验码还是四位?
checkNum = (checkNum / 65536 + checkNum % 65536);
return Integer.toHexString(checkNum);
}
}
- 运行结果
能够检验上个实验网桥的检验和
3. 程序中待解决的问题及改进的方向
- 没有实现将检验和写入到帧中,并由网桥判断检验和是否正确
五、实验分析
因为第7版的计算机网络课本没有网桥的内容,找了第6版的课本,并记录下来。
在数据链路层拓展以太网可以使用网桥(bridge)。网桥工作在数据链路层
,它根据MAC帧的目的地址对收到的帧进行转发和过滤
。当网桥收到一个帧时,并不是向所有的接口转发此帧,而是先检查此帧的目的MAC地址,然后再确定将该帧发到哪一个接口,或者是把它丢弃(即过滤)。
1、网桥的内部结构:
两个以太网通过网桥连接起来后,就成为一个覆盖范围更大的以太网,而原来的每个以太网就可以称为一个网段(segment)
【因此,网段只是网桥分隔的单位,同一个网段属于同一个局域网,不同网段也有可以属于同一个局域网】 。在图中所示的网桥,其接口1 和接口2 各连接到一个网段。
使用网桥带来的好处是:
- 过滤通信量,增大吞吐量:网桥能够隔离冲突域,使得吞吐量翻倍(假设一个网桥连接2个网段,每个网段的最大吞吐量是10Mb/s,那么使用网桥后的
最大
吞吐量是20Mb/s,而使用集线器或转发器,最大吞吐量只能是10Mb/s) - 扩大了物理范围
- 提高了可靠性:当网络出现问题时,一般只影响个别网段内
- 可互联不同物理层、不同MAC子层和不同速率的以太网:如连接10Mb/s和100Mb/s以太网
当然,网桥也有一些缺点,如:
- 由于网桥对接收的帧要先存储和查找转发表,然后才转发,而转发之前,还必须执行CSMA/CD算法(发生碰撞时要退避),这就
增加了时延
。 - 在MAC子层并
没有流量控制功能
。当网络上负荷很重时,网桥的缓存的存储空间可能不够而发生溢出,以致于产生帧丢失的现象 - 网桥只适合用户不多和通信量不大的以太网(一般不超过几百个用户),否则会因为传播过多而产生网络拥塞(广播风暴)
2、透明网桥:
目前使用得最多的网桥是透明网桥
(transparent bridge),“透
明”是指以太网上的站点并不知道所发送的帧将经过哪几个网桥,以太网上的站点
都看不见以太网上的网桥。
透明网桥还是一种即插即用设备
(plug-and-play device), 意思是只要把网桥接入局域网,不用人工配置
转发表网桥就能工作。这点很重要,因为虽然从理论上讲,网桥中的转发表可以用手工配置,但若以太网上的站点数很多,并且站点位置或网络拓扑也经常变化,那么人工配置转发表既耗时又很容易出错。
当网桥刚刚连接到以太网时, 其转发表是空的
。这时若网桥收到一个帧,它将怎样处理呢?网桥就按照以下自学习
(self-learning) 算法处理收到的帧(这样就逐步建立起转发表),并且按照转发表把帧转发出去。
我们来看看网桥的自学习:
(1) A 向B 发送帧连接在同一个局域网上的站点B 和网桥l 都能
收到A 发送的帧。
网桥l 先按源地址A 查找转发表。网桥1 的转发表中没有A 的地址,千是把地址A 和收到此帧的接口1 写入转发表中
。这就表示, 以后若收到要发给A 的帧, 就应当从这个接口1转发出去。接着再按目的地址B
查找转发表。转发表中没有B 的地址, 于是就通过除收到此帧的接口1 以外的所有接口(现在就是接口2) 转发该帧。
网桥2 从其接口1 收到这个转发过来的帧。
网桥2 按同样方式处理收到的帧。网桥2 的转发表中没有A 的地址,因此在转发表中写入地址A 和接口1 。网桥2 的转发表中没有B 的地址, 因此网桥2 通过除接收此帧的接口1 以外的所有接口(现在就是接口2) 转发这个帧。
请注意,现在两个网桥的转发表中已经各有了一个项目了。读者可能会问, B 本来就可以直接收到A 发送的帧,为什么还要让网桥l 和网桥2 盲目地转发这个帧呢?答案是:这两个网桥当时并不知道网络拓扑
,因此要通过自学习过程(不得不使用这种方式进行盲目转发)才能逐步弄清所连接的网络拓扑,建立起自己的转发表。
(2) F 向C 发送帧网桥2 从其接口2 收到这个帧。网桥2 的转发表中没有F, 因此在转发表写入地址F 和接口2 。网桥2 的转发表中没有C, 因此要通过网桥2 的接口1 把帧转发出去。现在C 和网桥l 都能收到这个帧。在网桥1 的转发表中没有F, 因此要把地址F和接口2 写入转发表,并且还要从网桥l 的接口1 转发这个帧。
(3) B 向A 发送帧网桥l 从其接口1 收到这个帧。网桥l 的转发表中没有B, 因此在转发表写入地址B 和接口1 。再查找目的地址A 。现在网桥1 的转发表中可以查到A, 其转发接口是1, 和这个帧进入网桥1 的接口一样。于是网桥1 知道,不用自己转发这个帧,A 也能收到B 发送的帧。于是网桥l 把这个帧丢弃,不再继续转发了。这次网桥l 的转发表增加了一个项目,网桥2 的转发表没有变化。
显然,如果网络上的每一个站都发送过帧,那么每一个站的地址最终都会记录在两个网桥的转发表上。
实际上,在网桥的转发表中写入的信息除了地址和接口外,还有帧进入该网桥的时间
(图3-25 和图3-28 中的转发表都省略了这一项)。为什么要登记进入网桥的时间呢?这是因为以太网的拓扑可能经常会发生变化,站点也可能会更换适配器(这样就改变了站点的地址)。另外,以太网上的工作站并非总是接通电源的。把每个帧到达网桥的时间登记下来,就可以在转发表中只保留网络拓扑的最新状态信息。具体的方法是,网桥中的接口管理软件周期性地扫描转发表中的项目。只要是在一定时间(例如几分钟)以前登记的都要删除。这样就使得网桥中的转发表能反映当前网络的最新拓扑状态。
由此可见,网桥中的转发表并非总是包含所有站点的信息。只要某个站点从来都不发送数据,那么在网桥的转发表中就没有这个站点的项目。如果站点A 在一段时间内不发送数据,那么在转发表中地址为A 的项目就被删除了
。
下面我们给出网桥的自学习和转发帧的一般步骤。
(1) 网桥收到一帧后先进行自学习。查找转发表中与收到帧的源地址有无相匹配的项目。如没有,就在转发表中增加一个项目(源地址、进入的接口和时间) 。如有,则把原有的项目进行更新。
(2) 转发帧。查找转发表中与收到帧的目的地址有无相匹配的项目。如没有,则通过所有其他接口(但进入网桥的接口除外)进行转发。如有,则按转发表中给出的接口进行转发。但应注意,若转发表中给出的接口就是该帧进入网桥的接口,则应丢弃这个帧(因为这时不需要经过网桥进行转发) 。
透明网桥还使用了一个生成树(spanning tree) 算法
, 即互连在一起的网桥在进行彼此通信后,就能找出原来的网络拓扑的一个子集。在这个子集里,整个连通的网络中不存在回路,即在任何两个站之间只有一条路径。
最后
以上就是爱听歌大树为你收集整理的计算机网络实验五 网络程序设计(网桥+检验和+网桥原理)一、 实验目的二、实验环境三、实验内容四、 项目设计报告五、实验分析的全部内容,希望文章能够帮你解决计算机网络实验五 网络程序设计(网桥+检验和+网桥原理)一、 实验目的二、实验环境三、实验内容四、 项目设计报告五、实验分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复