持续创作,加速成长!这是我参与「掘金日新计划 · 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();
}
实验结果