计网实验之模拟网桥(C++)

251 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

前言

本文源于计算机网络实验中一道比较有趣的题目——模拟网桥,在此记录一下网桥的原理、实现思路及代码,以及通过本次实验也让我回顾了逐渐陌生的C++(读写文件操作)。

题目——模拟网桥

写一个程序来模拟网桥功能。 模拟实现网桥的转发功能,以从文件中读取帧模拟网桥从网络中收到一帧,即从两个文件中读入一系列帧,从第一个文件中读入一帧然后从第二个文件中再读入一帧,如此下去。对每一帧,显示网桥是否会转发,及显示转发表内容。 要求: Windows或Linux环境下运行,程序应在单机上运行。 分析: 用程序模拟网桥功能,可以假定用两个文件分别代表两个网段上的网络帧数据。而两个文件中的数据应具有帧的特征,即有目的地址,源地址和帧内数据。程序交替读入帧的数据,就相当于网桥从网段中得到帧数据。 对于网桥来说,能否转发帧在于把接收到的帧与网桥中的转发表相比较。判断目的地址后才决定是否转发。由此可见转发的关键在于构造转发表。这里转发表可通过动态生成。

原理分析与图示

题意相当于模拟下图的网桥转发的过程,一个网桥连接两个网段的主机,通过转发表进行转发。 网桥转发原理: 包括两个步骤:自学习和转发帧。首先,网桥对两个端口发来的帧解析出源主机和目的主机的地址,在转发表中查找是否有源主机的端口信息,若找不到,网桥则在转发表中添加源主机的信息(源地址、进入的接口和时间)(由于是模拟实验,因此没有考虑时间这个要素)。【自学习】 之后,在转发表中查找是否有目的主机的端口信息,若找不到,则向除接收端口以外的端口转发帧,若找到,则需要判断目的主机和源主机是否处在端口的同一侧,若是,则丢弃帧,无需转发,否则,向转发表中指定的端口转发帧。【转发帧】

图示: 在这里插入图片描述

算法流程

说明: 此图来自于其他文章,当时感觉画得很好,直接拿来使用。 由于没有及时记录文章链接,无法注明图片来源,如果图片作者看到可以留言一下,抱歉!感谢! 在这里插入图片描述

代码设计思路

1、生成数据:利用随机数分配网段的主机,再生成随机的帧数据,并写入到两个文件中,分别代表在来两个端口的数据 2、核心算法:首先一次读取文件1一行数据,并解析成主机地址和发送的数据的信息。之后,先进入自学习阶段,查询转发表中是否有源主机,没有则更新转发表;接着判断是否需要转发帧,查询转发表中是否有目的主机的信息,找到目的主机则还行对比源主机和目的主机所属的网段,决定丢弃帧还是转发帧。到这里完成了从端口1的一帧数据的处理,端口2的处理同理。

代码中用到的数据结构: ①Data:定义了帧的结构(包含源主机名、目的主机名、传送的数据) ②Host:定义了假设的各网段上主机的分别情况(包含主机id,主机名,所连端口号等,并包含了初始化的方法等) ③forwarding_table:定义了转发表的结构(包含主机名和所属的端口号)

代码

代码源于博主自己编写的,本人能力有限,若有问题,欢迎指教!

#include <iostream>
#include <fstream>
#include <ctime>
#include <random>
#include <stdio.h>
#include <windows.h>
using namespace std;

#define file1 "bridge1.txt"
#define file2 "bridge2.txt"
#define hostNum 8  //定义8台主机
#define portNum 2  //定义端口
#define messageNum 10  //每个文件发送的帧数

int hostNumSeg1;  //网段1的主机数
int hostNumSeg2;  //网段2的主机数

struct Host {  //定义主机结构
	int *id;  //主机id(0-∞)
	char *name;  //主机名称(字母A-Z)
	int *port;  //主机所接网桥端口(1,2)
	int *segment1;  //网段1的主机
	int *segment2;  //网段2的主机
	void init() {  //给主机随机分配端口
		//printf("连接到网桥的主机有%d台\n", hostNum);
		id = new int[hostNum];
		port = new int[hostNum];
		name = new char[hostNum];
		srand(unsigned(time(NULL)));
		for (int i = 0; i < hostNum; ++i) {
			id[i] = i;
			name[i] = 'A' + i;
			port[i] = rand() % 2 + 1;
		}
		//画出网桥中主机分布状态
		int *list;
		list = new int[hostNum];  //查询两个端口的主机分布,端口为1的主机索引放在数组左侧,端口为2的主机索引放在数组右侧
		int pstart=-1, pend=hostNum;  //数组指针
		for (int i = 0; i < hostNum; ++i) {
			//printf("主机%c连接在端口%d...\n", name[i], port[i]);
			if (port[i] == 1) list[++pstart] = i;
			else list[--pend] = i;
		}
		hostNumSeg1 = pstart + 1;
		hostNumSeg2 = hostNum - pend;
		segment1 = new int[hostNumSeg1];  //网段1的主机数为pstart+1
		segment2 = new int[hostNumSeg2];  //网段2的主机数为hostNum-pend
		//printf("连接端口1的主机有:");
		for (int i = 0; i <= pstart; ++i) {
			//printf("%c ", name[list[i]]);
			segment1[i] = list[i];
		}
		//printf("\n");
		//printf("连接端口2的主机有:");
		for (int i = hostNum - 1; i >= pend; --i) {
			printf("%c ", name[list[i]]);
			segment2[hostNum - 1 - i] = list[i];
		}
		//printf("\n\n");
		//画图
		printf("主机和网桥的分布图如下:\n");
		for (int i = 0; i <= pstart; ++i) printf("%c ", name[list[i]]);
		printf("  1◤◥2  ");
		for (int i = hostNum - 1; i >= pend; --i) printf("%c ", name[list[i]]);
		printf("\n");
		delete[]list;
	}
	void drop() {
		delete[]id; delete[]name; delete[]port;
	}
};

struct Data {  ////定义帧数据结构
	char sourceHost;  //源主机
	char positionHost;  //目的主机
	char data[11];  //假设每一帧数据长度为10,数据随机生成
};

void fileWrite(Host host)  //
{
	Data data;
	//fstream fileExit;
	//fileExit.open(file1, ios::in);
	//if (fileExit) {
	//	printf("目录下有同名文件存在!删除文件...\n");
	//	remove(file1);  //文件存在,则删除原文件
	//}
	printf("\n网段1:");
	for (int i = 0; i < hostNumSeg1; ++i)
		printf("%c ", host.name[host.segment1[i]]);
	printf("\n");
	printf("\n网段2:");
	for (int i = 0; i < hostNumSeg2; ++i)
		printf("%c ", host.name[host.segment2[i]]);

	printf("\n\n写入网段1的帧数据...\n");
	ofstream file;  //声明一个输出流文件对象
	file.open(file1);  //向网段1文件写入数据
	for(int i=0; i < messageNum; i++) {  //网段1文件file1中生成10帧的数据
		data.sourceHost = host.name[host.segment1[rand() % hostNumSeg1]];  //随机取一台网段1的主机作为源主机
		printf("源主机:%c\t", data.sourceHost);
		while (true) {
			data.positionHost = host.name[rand() % hostNum];  //目的主机可以是两个网段的任意一台(除了自己)
			if (data.positionHost == data.sourceHost) continue;
			else break;
		}
		printf("目的主机:%c\t", data.positionHost);
		for (int i = 0; i < 10; ++i) data.data[i] = '0'+(rand() % 2);  //数据为0、1比特流(字符型)
		printf("数据:");
		for (int i = 0; i < 10; ++i)printf("%c",data.data[i]);
		printf("\n");
		file << data.sourceHost << " ";  //数据写入文件
		for (int i = 0; i < 10; ++i) file << data.data[i];
		file << " " << data.positionHost << '\n';
	}
	file.close();  //关闭io数据流
	
	printf("\n");

	printf("写入网段2的帧数据...\n");
	file.open(file2);  //向网段1文件写入数据
	for (int i = 0; i < messageNum; i++) {  //网段2文件file2中生成10帧的数据
		int host_idx = rand() % hostNumSeg2;
		data.sourceHost = host.name[host.segment2[host_idx]];  //随机取一台网段1的主机作为源主机
		printf("源主机:%c\t", data.sourceHost);
		while (true) {
			data.positionHost = host.name[rand() % hostNum];  //目的主机可以是两个网段的任意一台(除了自己)
			if (data.positionHost == data.sourceHost) continue;
			else break;
		}
		printf("目的主机:%c\t", data.positionHost);
		for (int i = 0; i < 10; ++i) data.data[i] = '0' + (rand() % 2);  //数据为0、1比特流(字符型)
		printf("数据:");
		for (int i = 0; i < 10; ++i)printf("%c", data.data[i]);
		printf("\n");
		file << data.sourceHost << " ";  //数据写入文件
		for (int i = 0; i < 10; ++i) file << data.data[i];
		file << " " << data.positionHost << '\n';
	}
	file.close();  //关闭io数据流
	printf("\n");
}

struct forwarding_table {  //转发表数据结构
	char *hostName;  //主机名
	int *port;  //端口号
	void init() {
		hostName = new char[hostNum];
		port = new int[hostNum];
	}
	void drop() {
		delete[]hostName;
		delete[]port;
	}
};

void sendData(Host host)
{
	forwarding_table table;  //建立一个转发表
	table.init();  //转发表初始化
	int table_idx = 0;  //转发表主机数
	ifstream file_1, file_2;
	file_1.open(file1);  //打开网段1帧数据文件
	file_2.open(file2);  //打开网段1帧数据文件
	Data data;
	for (int i = 0; i < messageNum; ++i)  //交替读取帧数据,转发数据,更新转发表
	{
		//读取网段1文件=========================================================
		char message[15];
		if (!file_1.eof()) {
			file_1.getline(message, 20);  //读取一帧的数据
			printf("读取帧数据信息:%s", message);
		}
		printf("\n");
		//解析message
		data.sourceHost = message[0];
		for (int i = 0; i < 10; ++i) data.data[i] = message[i + 2];
		data.data[10] = '\0';
		data.positionHost = message[13];
		//printf("帧数据解析结果:主机%c向主机%c发送数据%s\n", data.sourceHost, data.positionHost, data.data);
		
		//=========自学习阶段,查找转发表中是否有源主机的信息===========
		bool NotFound = true;
		for (int i = 0; i < table_idx; ++i)  //核心算法
		{
			if (data.sourceHost == table.hostName[i]) {  //在转发表中找到源主机
				NotFound = false;
				break;
			}
		}
		if (NotFound) {  //转发表中没有源主机信息
			table.hostName[table_idx] = data.sourceHost;
			table.port[table_idx++] = 1;  //记录来自端口1的主机
			printf("向转发表中添加主机%c...\n",data.sourceHost);
		}
		//=========转发帧判断=========
		NotFound = true;
		NotFound = true;
		for (int i = 0; i < table_idx; ++i)  //核心算法
		{
			if (data.positionHost == table.hostName[i]) {  //在转发表中找到目的主机
				NotFound = false;
				if (table.port[i] == 1) printf("目的主机%c和源主机%c在同一个网段,帧数据不转发...\n", data.positionHost, data.sourceHost);
				else printf("来自端口1的主机%c发送的数据通过端口2进行转发...\n", data.sourceHost);
				break;
			}
		}
		if (NotFound == true) { //在转发表中找不到主机,广播并更新转发表
			printf("找不到目的主机%c,向所有端口广播数据...\n",data.positionHost);
		}
		Sleep(500);

		//读取网段2文件=========================================================
		if (!file_2.eof()) {
			file_2.getline(message, 20);  //读取一帧的数据
			printf("读取帧数据信息:%s", message);
		}
		printf("\n");
		//解析message
		data.sourceHost = message[0];
		for (int i = 0; i < 10; ++i) data.data[i] = message[i + 2];
		data.data[10] = '\0';
		data.positionHost = message[13];
		//printf("帧数据解析结果:主机%c向主机%c发送数据%s\n", data.sourceHost, data.positionHost, data.data);
		//=========自学习阶段,查找转发表中是否有源主机的信息===========
		NotFound = true;
		for (int i = 0; i < table_idx; ++i)  //核心算法
		{
			if (data.sourceHost == table.hostName[i]) {  //在转发表中找到源主机
				NotFound = false;
				break;
			}
		}
		if (NotFound) {  //转发表中没有源主机信息
			table.hostName[table_idx] = data.sourceHost;
			table.port[table_idx++] = 1;  //记录来自端口1的主机
			printf("向转发表中添加主机%c...\n",data.sourceHost);
		}
		//=========转发帧判断=========
		NotFound = true;
		for (int i = 0; i < table_idx; ++i)  //核心算法
		{
			if (data.positionHost == table.hostName[i]) {  //在转发表中找到目的主机
				NotFound = false;
				if (table.port[i] == 2) printf("目的主机%c和源主机%c在同一个网段,帧数据不转发...\n", data.positionHost, data.sourceHost);
				else printf("来自端口2的主机%c发送的数据通过端口1进行转发...\n", data.sourceHost);
				break;
			}
		}
		if (NotFound == true) { //在转发表中找不到主机,广播并更新转发表
			printf("找不到目的主机%c,向所有端口广播数据...\n",data.positionHost);
		}

		Sleep(500);
		//打印转发表
		//printf("当前转发表:\n");
		//for (int i = 0; i < table_idx; ++i) {
		//	printf("主机%c\t端口%d\n", table.hostName[i], table.port[i]);
		//}
		printf("\n");
	}
	//打印转发表
	printf("\n当前转发表:\n");
	for (int i = 0; i < table_idx; ++i) {
		printf("主机%c\t端口%d\n", table.hostName[i], table.port[i]);
	}
	printf("\n");
	file_1.close();
	file_2.close();
	table.drop();
}

int main()
{
	Host host;
	host.init();
	fileWrite(host);
	sendData(host);
	host.drop();
}

实验结果

在这里插入图片描述 在这里插入图片描述在这里插入图片描述

文件数据

在这里插入图片描述在这里插入图片描述