计网实验之模拟网桥&C++读写操作

前言

本文源于计算机网络实验中一道比较有趣的题目——模拟网桥,在此记录一下网桥的原理、实现思路及代码,以及通过本次实验也让我回顾了逐渐陌生的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();
}

实验结果

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

文件数据

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

  • 8
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,下面是用 C++ 写的一个简单的程序来模拟网桥的基本功能: ```c++ #include <iostream> #include <unordered_map> #include <vector> using namespace std; class Bridge { private: int bridge_id; unordered_map<string, int> mac_table; vector<pair<int, string>> port_states; public: Bridge(int id) { bridge_id = id; } void receive_frame(int port, const string& src_mac, const string& dst_mac, const string& data) { // Learn source MAC address mac_table[src_mac] = port; // Check destination MAC address if (mac_table.count(dst_mac) > 0) { int dst_port = mac_table[dst_mac]; if (dst_port != port) { // Forward frame to destination port forward_frame(dst_port, src_mac, dst_mac, data); } } else { // Flood frame to all ports except source port for (auto& p : port_states) { if (p.first != port) { forward_frame(p.first, src_mac, dst_mac, data); } } } } void forward_frame(int port, const string& src_mac, const string& dst_mac, const string& data) { cout << "Bridge " << bridge_id << " forwarding frame to port " << port << ": src_mac=" << src_mac << ", dst_mac=" << dst_mac << ", data=" << data << endl; port_states[port].second = "forwarding"; } void add_port(int port_id, const string& state) { port_states.push_back(make_pair(port_id, state)); } void remove_port(int port_id) { for (auto it = port_states.begin(); it != port_states.end(); ++it) { if (it->first == port_id) { port_states.erase(it); break; } } } }; // Example usage: int main() { Bridge bridge1(1); bridge1.add_port(1, "forwarding"); bridge1.add_port(2, "forwarding"); string src_mac1 = "00:11:22:33:44:55"; string dst_mac1 = "aa:bb:cc:dd:ee:ff"; string data1 = "Hello world!"; bridge1.receive_frame(1, src_mac1, dst_mac1, data1); string src_mac2 = "aa:bb:cc:dd:ee:ff"; string dst_mac2 = "00:11:22:33:44:55"; string data2 = "Goodbye!"; bridge1.receive_frame(2, src_mac2, dst_mac2, data2); return 0; } ``` 这个程序定义了一个 `Bridge` 类来表示一个网桥,它可以添加/移除端口并接收/转发帧。网桥维护一个 MAC 地址表来学习每个源 MAC 地址对应的端口,并根据目的 MAC 地址将帧转发到正确的端口。如果目的 MAC 地址没有在表中找到,则将帧泛洪到所有端口(除源端口外)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浅若清风cyf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值