题目:写一个程序来模拟网桥功能。
模拟实现网桥的转发功能,以从文件中读取帧模拟网桥从网络中收到一帧,即从两个文件中读入一系列帧,从第一个文件中读入一帧然后从第二个文件中再读入一帧,如此下去。对每一帧,显示网桥是否会转发,及显示转发表内容。
- 要求:Windows或Linux环境下运行,程序应在单机上运行。
- 分析:
1. 用程序模拟网桥功能,可以假定用两个文件分别代表两个网段上的网络帧数据。而两个文件中的数据应具有帧的特征,即有目的地址,源地址和帧内数据。程序交替读入帧的数据,就相当于网桥从网段中得到帧数据。
2. 对于网桥来说,能否转发帧在于把接收到的帧与网桥中的转发表相比较。判断目的地址后才决定是否转发。由此可见转发的关键在于构造转发表。这里转发表可通过动态生成。
本地数据样式
注意文档中不要有多余的空格,换行。每个字符串中间是一个tab键不是8个空格键。
第一次运行时,转发表可以什么内容都没有
-
网段一
-
网段二
-
转发表
分析
- 理解好网桥的功能,代码实现并不困难(当然我的代码并不是完全符合网桥的逻辑的,是一个最简单的网桥功能实现);
- 这个实验让我们实现网桥的自学习和转发帧
- 自学习:查找转发表中与收到帧的源地址有无相匹配的项目。
-如果有,更新原有项目(简单的代码实现就更新一下时间);
-如果没有,新增一个项目。 - 转发帧:查找转发表中与收到帧的目的地址有无相匹配的项目。
- 如果有,按转发表给出接口进行转发;
- 如果没有,通过所有接口进行转发;
- 若转发表给出的接口就是该帧进入网桥的接口,丢弃该帧。
- 自学习:查找转发表中与收到帧的源地址有无相匹配的项目。
程序流程图
代码
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <Windows.h>
#include <cstdio>
#include <time.h>
using namespace std;
//获取string类型的本地时间
string GetSystemTimeStr()
{
struct tm t; //tm结构指针
time_t now; //声明time_t类型变量
time(&now); //获取系统日期和时间
localtime_s(&t, &now); //获取当地日期和时间
string time;
time.append(to_string(t.tm_year+1900));
time.append(".");
time.append(to_string(t.tm_mon+1));
time.append(".");
time.append(to_string(t.tm_mday));
time.append(" ");
time.append(to_string(t.tm_hour));
time.append(":");
time.append(to_string(t.tm_min));
time.append(":");
time.append(to_string(t.tm_sec));
return time;
}
//查表,自学习并进行转发
bool forword(map<string, pair<string, string>>& forwordingTable, string frame, int flag) {
//如果是读到文件中的第一行,返回
if (frame == "source\tdestn\tdata") return false;
cout << "网桥接口" << flag << "接收到帧数据" << endl;
//网桥解析帧数据
int firstTabIndex = frame.find_first_of("\t");
int lastTabIndex = frame.find_last_of("\t");
//源地址
string source = frame.substr(0, firstTabIndex);
//目的地址
string destination = frame.substr(firstTabIndex + 1, lastTabIndex - firstTabIndex - 1);
//帧内数据
string data = frame.substr(lastTabIndex + 1, frame.size() - lastTabIndex);
//帧离开源地址,到达网桥(按道理这语句应该在最上面,但是要用到几个变量只能放在这里)
cout << "PC" << source << " send data to " << "PC" << destination << ": " << data << endl;
//查转发表
//自学习,先用源地址更新转发表
map<string, pair<string, string>>::iterator iter = forwordingTable.find(source);
//表中没有匹配,新增项目
if (iter == forwordingTable.end()) {
pair<string, string> save(to_string(flag), GetSystemTimeStr());
forwordingTable.insert(pair<string, pair<string, string>>(source, save));
}
//表中有匹配,更新项目
else forwordingTable[source].second = GetSystemTimeStr();
//转发帧,判断转发表中是否有目的地址
iter = forwordingTable.find(destination);
//没有有目的地址,通过所有接口进行转发;有目的地址,按给定接口进行转发
if (iter == forwordingTable.end()) cout << "转发表中无PC" << destination << "的信息,网桥通过所有接口进行转发" << endl;
else if (forwordingTable[destination].first == to_string(flag)) cout << "PC" << source << "和PC" << destination << "在同一网段下,丢弃该帧数据" << endl;
else cout << "从接口" << flag << "处进行转发" << endl;
//帧离开网桥网桥,到达目的地址
cout << "PC" << destination << " receive data from " << source << ": " << data << endl << endl;
return true;
}
int main() {
//读取两网段和转发表文件
ifstream segment1File("segment1.txt");
if (!segment1File.is_open()) {
cout << "segmeng1文件打开失败!" << endl;
return 1;
}
ifstream segment2File("segment2.txt");
if (!segment2File.is_open()) {
cout << "segmeng2文件打开失败!" << endl;
return 1;
}
ifstream forwordingTableFileRead("forwordingTable.txt");
if (!forwordingTableFileRead.is_open()) {
cout << "forwordingTable文件打开失败!" << endl;
return 1;
}
//用地图存起来转发表
map<string, pair<string,string>> forwordingTable;
string row;
while (getline(forwordingTableFileRead, row))
{
//忽略第一行标题
if (row == "PC\tsegment\ttime") continue;
//找到制表符位置
int firstTabIndex = row.find_first_of('\t');
int lastTabIndex = row.find_last_of("\t");
//获取map的key和value,插入map
string key = row.substr(0, firstTabIndex);
string value = row.substr(firstTabIndex +1, lastTabIndex - firstTabIndex - 1);
string time = row.substr(lastTabIndex + 1, row.size() - lastTabIndex);
pair <string, string> save(value, time);
forwordingTable.insert(pair<string, pair<string,string>>(key, save));
}
//帧数据
string frame;
//交替读入标志
int flag = 1;
//网桥开始工作
while (!segment1File.eof() || !segment2File.eof()) {
if (flag == 1) {
if (!segment1File.eof()) {
//读网段一中的一行
getline(segment1File, frame);
if(!forword(forwordingTable, frame, flag)) continue;
}
//交替
flag = 2;
}
if (flag == 2) {
if (!segment2File.eof()) {
//读网段二中的一行
getline(segment2File, frame);
if (!forword(forwordingTable, frame, flag)) continue;
}
//交替
flag = 1;
}
}
//更新转发表到本地
ofstream forwordingTableFileWrite("forwordingTable.txt");
if (!forwordingTableFileWrite.is_open()) {
cout << "forwordingTable文件打开失败!" << endl;
return 1;
}
forwordingTableFileWrite << "PC\tsegment\ttime" << endl;
for (auto mapIndex : forwordingTable) {
forwordingTableFileWrite << mapIndex.first << "\t" << mapIndex.second.first << "\t" << mapIndex.second.second<<endl;
}
//完成工作,关闭文件
segment1File.close();
segment2File.close();
forwordingTableFileRead.close();
forwordingTableFileWrite.close();
return 0;
}
结果截图
-
第一次转发表中没有任何信息
-
第二次转发表中已经有了全部信息