一、实验目的
理解网桥功能及计算机如何处理计算一个文件的16位校验和

二、使用仪器、器材
Window10操作系统计算机、visual studio 2019

三、实验内容及原理
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 位,那么把进位一起加到最后的效验和中。

四、实验过程原始数据记录

实验一

#include <iostream>
#include <fstream>
using namespace std;
#define TABLEMAX 255 //转发表最大行数
int Count = 0;//转发表的行数
struct dataframe
{
	char source;		//源地址	
	char destination;	//目的地址	
	int data;			//数据
};
struct Forwardingtable
{
	int interface;	//接口
	char destination;//目的地址
}table[TABLEMAX] = {0};
int searchtable(dataframe dataframe)	//查找转发表,返回flag的值
{
	int temp1=0, temp2=0;//设置两个临时变量存储接口值
	int low = 0, high = Count-1,mid;
	if (Count < 8)
	{
		for (int i = 0; i < Count; i++)
		{
			if (dataframe.source == table[i].destination)
			{
				temp1 = table[i].interface;
			}
			if (dataframe.destination == table[i].destination)
			{
				temp2 = table[i].interface;
			}
		}
	}
	while (Count>=8&&low <= high)
	{
		mid = (low + high) / 2;
		if (dataframe.source == table[mid].destination)
		{
			temp1 = table[mid].interface; break;
		}
		if (dataframe.source > table[mid].destination)
		{
			low = mid + 1;
		}
		if (dataframe.source < table[mid].destination)
		{
			high = mid - 1;
		}
	}
	low = 0; high = Count-1;
	while (Count >= 8 && low <= high)
	{
		mid = (low + high) / 2;
		if (dataframe.destination == table[mid].destination)
		{
			temp2 = table[mid].interface; break;
		}
		if (dataframe.destination > table[mid].destination)
		{
			low = mid + 1;
		}
		if (dataframe.destination < table[mid].destination)
		{
			high = mid - 1;
		}
	}
	
	if (temp1 && temp2 && temp1 == temp2) //在同一接口,不转发
	{
		return 0;
	}
	else if (temp1 && temp2)//不在同一接口,转发
	{
		return temp2;
	}
	else//转发表内无法查到数据,需要更新转发表
	{
		return 7;
	}
}
void updatatable(dataframe dataframe,int i)//更新转发表
{
	table[Count].destination = dataframe.source;//更新地址
	table[Count].interface = i;					//更新接口
	Count++;									//转发表最大行数加1
	if (Count == 8)
	{
		for (int i = 0; i < Count; i++)
		{
			for (int j = 1; j < (Count - i); j++)
			{
				if (table[j - 1].destination > table[j].destination)
				{
					Forwardingtable temp;
					temp = table[j - 1];
					table[j - 1] = table[j];
					table[j] = temp;
				}
			}
		}
	}
}
void showtable()
{
	cout << "------------------------------------------------------------------" << endl;
	cout << "当前转发表如下:" << endl;
	cout << "地址" << '\t' << "接口" << endl;
	for (int i = 0; i < Count; i++)
	{
		cout << table[i].destination << '\t' << table[i].interface << endl;
	}
	cout << endl;
}
int main() 
{
	int flag;//设置标志值
	fstream segment1("C:\\Users\\suki\\Desktop\\segment1.txt");
	fstream segment2("C:\\Users\\suki\\Desktop\\segment2.txt");
	dataframe frame;
	while (!segment1.eof()&&!segment2.eof())
	{
		segment1 >> frame.source >> frame.destination>>frame.data;	//从文件内读入数据
		flag = searchtable(frame);//更新flag的值
		switch (flag)
		{
		case 0:showtable();
			cout << "帧(源地址为:" << frame.source << "目的地址为:" << frame.destination <<
			"数据为:" << frame.data << ")在同一接口,不转发" << endl;  break;
		case 7:showtable();
			cout << "帧(源地址为:" << frame.source << "目的地址为:" << frame.destination <<
			"数据为:" << frame.data << ")在转发表内无法查到数据,无法转发!!" << endl;
			updatatable(frame,1);  break;
		default:showtable();
			cout << "帧(源地址为:" << frame.source << "目的地址为:" << frame.destination <<
			"数据为:" << frame.data << ")转发至接口:" << flag << endl;  break;
		}
		segment2 >> frame.source >> frame.destination >> frame.data;	//从文件内读入数据
		flag = searchtable(frame);//更新flag的值
		switch (flag)
		{
		case 0:showtable();
			cout << "帧(源地址为:" << frame.source << "目的地址为:" << frame.destination <<
			"数据为:" << frame.data << ")在同一接口,不转发" << endl;  break;
		case 7:showtable();
			cout << "帧(源地址为:" << frame.source << "目的地址为:" << frame.destination <<
			"数据为:" << frame.data << ")在转发表内无法查到数据,无法转发!!" << endl;
			updatatable(frame,2);  break;
		default:showtable();
			cout << "帧(源地址为:" << frame.source << "目的地址为:" << frame.destination <<
			"数据为:" << frame.data << ")转发至接口:" << flag << endl;  break;
		}
	}
}

实验二

#pragma warning(disable:4996);
#include<iostream>
#include<fstream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
int main()
{
	FILE* fp;
	fp = fopen("C:\\Users\\suki\\Desktop\\infile.txt", "rb");
	if (!fp)
	{
		cout << "读取文件失败!"<<endl;
	}
	else
	{
		cout << "读取文件成功!"<< endl;
	}
	char ch;//读入字符串
	int count = 0;//计数器,偶数时对两个字符进行处理
	int a[6] = {};
	int i = 0;
	int temp=0;
	int z = 1;
	while (z)
	{		
		if ((ch = fgetc(fp)) != EOF)
		{			
			count++;
			if (count % 2 != 0)
			{
				temp = ch;
			}
			else
			{
				a[i] = (temp << 8) + (int)ch;
				i++;		
			}
		}
		else
		{
			z = 0;
		}
	}
	int checksum = 0;//校验和
	temp = 0;
	for (int j= 0; j < 6; j++)
	{
		temp = temp + a[j];
	}
	int carry = 0;//进位
	carry = temp / 65536;
	checksum = temp % 65536 + carry;
	cout << "得到的十进制数:" << checksum << endl;
	cout << "转化为十六进制数:";
	printf("%x", checksum);
}

实验结果截图
实验一:
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
实验二:
在这里插入图片描述
五、实验分析
文件segment1和segment2内的数据
在这里插入图片描述
在search函数里判断不在同一接口时转发的返回值是目的地址的接口,可能是1也可能是2,所以必须放在switch函数里的default里比较方便,不用另外设case 1和case 2
记事本里帧的数据(源地址,目的地址)不能随便设置,不然转发表建立会混乱,假如在文件1中有AG3(源地址为A,目的地址为G,数据为3),此时我的转发表更新函数会将A的接口更新为1,但是如果文件2中有AH2(源地址为A,目的地址为H,数据为2),此时A的接口会更新为2,这样的话更新表就会发生创建混乱。
在实验一中,转发表以下图的网桥来创建
在这里插入图片描述
代码中用了二分法进行查找转发表。但是必须在转发表填满之后才能排序再用二分法,二分法需要排好序才能用

在实验二中,fopen函数使用出现以下错误
在这里插入图片描述
这是因为在visual studio2019中认为fopen函数已经不太安全了,考虑到只是进行实验,不用过多注重安全问题,解决办法就是在首部添加代码:#pragma warning(disable:4996);即可

Logo

更多推荐