计算机网络实验五:网络程序设计
一、实验目的理解网桥功能及计算机如何处理计算一个文件的16位校验和二、使用仪器、器材Window10操作系统计算机、visual studio 2019三、实验内容及原理1、 写一个程序来模拟网桥功能。模拟实现网桥的转发功能,以从文件中读取帧模拟网桥从网络中收到一帧,即从两个文件中读入一系列帧,从第一个文件中读入一帧然后从第二个文件中再读入一帧,如此下去。对每一帧,显示网桥是否会转发,及显示转发表
一、实验目的
理解网桥功能及计算机如何处理计算一个文件的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);即可
更多推荐
所有评论(0)