📄 framehandler.cpp
字号:
#include "framehandler.h"
#include <iostream>
#include <cstring>
#include <iomanip>
using std::cout;
using std::endl;
using std::ios_base;
using std::setw;
using std::cin;
using std::ios;
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 构造函数
// 参数:
// bool isPackging : 该参数表示当前实例要执行何种操作,为true表示[帧封装],为false表示[帧解析]
// const char* filePath : 根据isPackaging的不同,该参数有不同意义
// 当前者为true时,则将封装好的帧保存到filePath指定的文件中
// 当前者为false时,则从filePath指定的文件中解析Ethernet帧
////////////////////////////////////////////////////////////////////////////////////////////////////////////
FrameHandler::FrameHandler(bool isPackaging, const char* filePath)
{
// 设置操作类型,true表示帧封装,false表示帧解析
this->isPackaging = isPackaging;
// 保存文件路径
// 当进行帧封装时,表示封装后的帧所存放的文件路径;当进行帧解析时,表示待解析的文件的路径
this->filePath = new char[strlen(filePath) + 1];
strcpy(this->filePath, filePath);
// 对hexAlpha[]数组进行赋值
for (int i = 0; i < 10; i++)
{
hexAlpha[i] = char ('0' + i);
}
for (i = 10; i < 16; i++)
{
hexAlpha[i] = char ('A' + i - 10);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
// 析构函数
////////////////////////////////////////////////////////////////////////////////////////////////
FrameHandler::~FrameHandler()
{
// 释放资源
if (filePath != NULL)
{
delete[] filePath;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
// 该函数为对外接口,可被FrameHandler的实例直接调用
// 功能:
// 完成[帧封装]操作,并将封装后的帧保存在filePath指定的文件中
// 参数:
// void
// 返回值:
// 如果封装成功,则返回true
// 如果封装失败,则返回false
///////////////////////////////////////////////////////////////////////////////////////////////
bool FrameHandler::package()
{
// 如果创建实例时指定的操作类型不是“帧封装”,则调用该函数失败
if (isPackaging == false)
{
return false;
}
// 下面进行帧封装的操作
// 输出操作信息
cout << "操作类型:帧封装\n保存封装结果的文件路径:" << filePath
<< "\n\n==================================================\n\n";
// 让用户输入一段信息,以回车结束
cout << "请输入一段信息,以回车结束\n(如果直接输入回车,则可以从文件中读取数据,并将其封装成Ethernet帧):" << endl;
char* data = new char[MAX_DATA_LENGTH + 1];
cin.getline(data, MAX_DATA_LENGTH + 1);
// 如果用户直接输入了回车(即data中不含任何数据),则认为用户要从文件中加载数据,提示用户输入文件的路径
if (strlen(data) == 0)
{
// 从文件中加载数据
if (!loadDataFromFile(data))
{
cout << "\n从文件中加载数据失败!\n";
delete[] data;
return false;
}
else
{
cout << "\n==================================================\n"
<< "从文件中读入的数据如下:\n\n" << data << endl;
}
}
// 填充前导符、帧前定位符、目的MAC、源MAC、数据长度等信息
FrameFront frameFront;
setFrameFront(frameFront, data);
// 计算CRC校验码(需要用到目的MAC、源MAC、数据长度、数据段、以及填充字节(如果有的话))
int totalBytes = (strlen(data) < MIN_DATA_LENGTH) ? MIN_DATA_LENGTH : strlen(data);
unsigned char* dataWithFillBytes = new unsigned char[totalBytes];
memcpy(dataWithFillBytes, data, strlen(data));
if (strlen(data) < MIN_DATA_LENGTH)
{
memset(dataWithFillBytes + strlen(data), 0, MIN_DATA_LENGTH - strlen(data));
}
unsigned char crc = getCRC(frameFront, dataWithFillBytes);
// 将Ethernet帧保存到文件filePath中
if (!storeFrameInFile(frameFront, data, crc))
{
cout << "\n向文件 [" << filePath << "] 写入数据失败!\n";
return false;
}
else
{
// 写入成功,释放所有资源并返回true
delete[] data;
cout << "\n==================================================\n";
cout << "帧封装完毕!\n封装后的Ethernet帧存放在文件 [" << filePath << "] 中!\n\n";
return true;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 该函数为对外接口,可被FrameHandler的实例直接调用
// 功能:
// 完成[帧解析]操作,将filePath指定的文件进行解析,从中提取出Ethernet帧的相关信息
// 参数:
// void
// 返回值:
// 如果分析文件成功,则返回true
// 如果在处理文件的过程中出现任何错误,则返回false
////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool FrameHandler::unpackage()
{
// 如果创建实例时指定的操作类型不是“帧解析”,则调用该函数失败
if (isPackaging == true)
{
return false;
}
// 下面进行帧解析的操作
// 以二进制读的方式打开待解析的文件
FILE* file = fopen(filePath, "rb");
// 如果打开文件失败,则输出错误信息并返回false值
if (NULL == file)
{
cout << "\n无法打开文件 [" << filePath << "] !请确认文件路径是否正确!\n";
return false;
}
// 如果成功的打开了文件,则执行下面的操作
// 输出提示信息
cout << "操作类型:Ethernet帧解析\n待解析的文件路径:" << filePath << endl;
int frameCounter = 0; // 记录已经解析的帧的数目
FrameFront frameFront; // 保存读入的帧的前面部分(即前导符、界定符、目的mac、源mac、数据长度)
int count = sizeof(frameFront) / sizeof(unsigned char); // FrameFront结构体包含的字节数
// 从文件中读取数据并分析,直到文件尾或者发生错误
while (count == fread(&frameFront, sizeof(unsigned char), count, file))
{
if (isLegalPreamble(frameFront.preamble))
{
frameCounter++;
cout << "\n=========================第" << frameCounter << "个帧=========================\n";
// 设定输出格式为左对齐
cout.setf(ios::left);
// 输出帧的序号
cout << setw(WIDTH) << "序号" << ":" << frameCounter << endl;
// 输出前导码、帧前定位符、目的地址、源地址、数据长度等
outputFrameFront(frameFront);
// 计算数据段的长度(不包括填充字节)
int dataLength = (frameFront.dataLength[0] << 8) + frameFront.dataLength[1];
// 计算数据段与填充字节的总长度
int totalBytes = (dataLength < MIN_DATA_LENGTH) ? MIN_DATA_LENGTH : dataLength;
// 读取数据段与填充字节(如果有的话)
unsigned char* data = new unsigned char[totalBytes];
fread(data, sizeof(unsigned char), totalBytes, file);
// 输出数据段的有效内容(不包括填充字节)
cout << setw(WIDTH) << "数据字段" << ":";
char* dataToShow = new char[dataLength + 1];
memcpy(dataToShow, data, dataLength);
dataToShow[dataLength] = 0;
cout << dataToShow << endl;
delete[] dataToShow;
// 获取帧中的CRC校验字段
unsigned char crcInFrame;
fread(&crcInFrame, sizeof(unsigned char), 1, file);
// 计算CRC校验码(需要用到目的MAC、源MAC、数据长度、以及具体的数据段)
unsigned char realCRC = getCRC(frameFront, data);
// 输出CRC校验信息,以及是否接收等
cout << setw(WIDTH) << "CRC校验" << ":";
if (crcInFrame == realCRC)
{
cout << "(正确):" << hexAlpha[crcInFrame >> 4] << hexAlpha[crcInFrame & 0x0F] << endl;
cout << setw(WIDTH) << "状态" << ":Accept" << endl;
}
else
{
// 输出 (错误):[原校验码] 应为 [正确的校验码]
cout << "(错误):[" << hexAlpha[crcInFrame >> 4] << hexAlpha[crcInFrame & 0x0F] << "] 应为 ["
<< hexAlpha[realCRC >> 4] << hexAlpha[realCRC & 0x0F] << "]" << endl;
cout << setw(WIDTH) << "状态" << ":Discard" << endl;
}
// 释放资源
delete[] data;
// 继续寻找下一个帧
continue;
}
else
{
fseek(file, -(count - 1), ios_base::cur);
}
}
// 解析完毕,输出发现的帧的数目
if (frameCounter == 0)
{
cout << "\n==================================================\n";
cout << "解析完毕,但没有发现任何Ethernet帧!\n\n";
}
else
{
cout << "\n==================================================\n";
cout << "解析完毕,共找到上述" << frameCounter << "个Ethernet帧!\n\n";
}
// 关闭文件,然后返回
fclose(file);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 内部辅助函数
// 功能:
// 检查读入的数据是否包含正确的前导符以及帧前定位符,正确值应为十六进制的AA-AA-AA-AA-AA-AA-AA-AB
// 参数:
// const unsigned char* preamble : 该参数为传入值,表示7个字节的前导码 + 1个字节的帧前定位符
// 返回值:
// 如果前导码为十六进制的AA-AA-AA-AA-AA-AA-AA,且 帧前定位符为十六进制的AB,则返回true
// 否则返回false
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool FrameHandler::isLegalPreamble(const unsigned char* preamble)
{
// 首先检查前7个字节是否为十六进制的AA-AA-AA-AA-AA-AA-AA
for (int i = 0; i < 6; i++)
{
if(preamble[i] != 0xAA)
{
return false;
}
}
// 再检查最后一个字节是否为十六进制的AB
return (preamble[7] == 0xAB) ? true : false;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -