題目:寫一個程序來模擬網橋功能。
模擬實現網橋的轉發功能,以從文件中讀取幀模擬網橋從網絡中收到一幀,即從兩個文件中讀入一系列幀,從第一個文件中讀入一幀然后從第二個文件中再讀入一幀,如此下去。對每一幀,顯示網橋是否會轉發,及顯示轉發表內容。
- 要求: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;
}
結果截圖
-
第一次轉發表中沒有任何信息
-
第二次轉發表中已經有了全部信息