📄 framehandler.cpp
字号:
///////////////////////////////////////////////////////////////////////////////////////////////////////
// 内部辅助函数
// 功能:
// 根据数据段内容来计算crc校验码(此处采用CRC-8,即校验和为1Byte),通过调用generateCRC()完成
// 参数:
// const FrameFront& frameFront : 该结构体为传入值,它保存着Ethernet帧的前导码、帧前定位符、
// 目的地址、源地址、数据段长度等字段,在计算CRC过程中,需要
// 用到该结构体的destMAC、srcMAC、dataLength共3个字段
// const char* data : 该参数为传入值,它保存着Ethernet帧的数据段部分
// 返回值:
// 根据data计算出来的crc校验码(大小为1个字节)
///////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char FrameHandler::getCRC(const FrameFront& frameFront, const unsigned char* data)
{
// 计算CRC校验和所需要的数据是:目的MAC、源MAC、数据长度、数据段、以及填充字节(如果有的话)
int realDataLength = (frameFront.dataLength[0] << 8) + frameFront.dataLength[1];
int totalDataBytes = (realDataLength < MIN_DATA_LENGTH) ? MIN_DATA_LENGTH : realDataLength; // data的总长度,即有效数据
// 的长度 + 填充字节的长度
// 计算CRC所需数据的长度
int totalLength = sizeof(frameFront) - sizeof(frameFront.preamble) + totalDataBytes;
// 计算CRC所需的数据
unsigned char* dataForCRC = new unsigned char[totalLength];
memcpy(dataForCRC, frameFront.destMAC, sizeof(frameFront) - sizeof(frameFront.preamble));
memcpy(dataForCRC + sizeof(frameFront) - sizeof(frameFront.preamble), data, totalDataBytes);
// 调用generateCRC函数来计算CRC校验码
return generateCRC(dataForCRC, totalLength);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 内部辅助函数,在getCRC()函数中被调用
// 功能:
// 计算CRC校验码
// 参数:
// const unsigned char* dataForCRC : 计算CRC校验码所需要的输入数据
// int length : 数据的长度(单位:Bytes)
// 返回值:
// CRC校验码(1 Byte)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char FrameHandler::generateCRC(const unsigned char* dataForCRC, int length)
{
int totalLength = length + 1;
unsigned char* input = new unsigned char[totalLength];
memcpy(input, dataForCRC, length);
input[totalLength - 1] = 0; // 在输入数据的末尾添加一个字节0
// 计算input字段的CRC校验码
unsigned char crc = 0; // crc校验码
unsigned char genPoly = 0x07; // 使用的生成多项式为G(X) = X8 + X2 + X1 + 1
for (int i = 0; i < totalLength; i++)
{
for (unsigned char bitMask = 0x80; bitMask != 0; bitMask >>= 1)
{
if (crc & 0x80) // 首位是1
{
crc <<= 1;
if ((input[i] & bitMask) != 0) // 新输入的一位为1
{
crc |= 0x01;
}
crc ^= genPoly;
}
else // 首位是0
{
crc <<= 1;
if ((input[i] & bitMask) != 0) // 新输入的一位为1
{
crc |= 0x01;
}
}
}
}
return crc;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 内部辅助函数,该函数在package()中被调用
// 功能:
// 从fileName指定的文件中读取数据,放到字符数组data中
// 参数:
// char* data : 存放待封装的数据,这些数据是从指定的文件中读取的,而不是由用户在控制台直接输入
// 该参数为传出值
// 返回值:
// 如果成功的将文件中的数据加载到字符数组data中,则返回true
// 如果处理文件时发生了任何错误,则返回false
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool FrameHandler::loadDataFromFile(char* data)
{
cout << "\n==================================================\n";
cout << "您选择了从文件中加载数据!\n请输入待封装的数据所在文件的文件名:";
char* dataFileName = new char[257]; // 限定文件名的最大长度为256字节
cin.getline(dataFileName, 257); // 获取文件名
// 打开数据文件
FILE* dataFile = fopen(dataFileName, "rb");
if (dataFile == NULL)
{
cout << "\n打开文件 [" << dataFileName << "] 失败!请确认文件名是否正确!\n";
return false;
}
delete[] dataFileName; // 文件名不再被使用,将其释放
// 如果成功打开数据文件,则将其内容加载到字符数组data中
// 首先获取数据文件的大小
fseek(dataFile, 0, ios_base::end);
int dataFileSize = ftell(dataFile);
fseek(dataFile, 0, ios_base::beg);
// 如果数组data能容纳整个文件,则全部读入;否则,将文件内容在MAX_DATA_LENGTH(即1500Bytes)后截断
if (dataFileSize <= MAX_DATA_LENGTH)
{
fread(data, sizeof(char), dataFileSize, dataFile);
data[dataFileSize] = '\0';
}
else
{
fread(data, sizeof(char), MAX_DATA_LENGTH, dataFile);
data[MAX_DATA_LENGTH] = '\0';
cout << "\n文件过大,读入数据在" << MAX_DATA_LENGTH << "字节后进行截断!\n";
}
fclose(dataFile);
return true;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 内部辅助函数,该函数在package()中被调用
// 功能:
// 填写Ethernet帧的前导码、帧前定位符、目的MAC、源MAC、数据段长度等字段
// 参数:
// FrameFront& frameFront : 要设置的Ethernet帧(数据段之前的部分),该参数为传出值
// const char* data : Ethernet帧的数据段,需要用该参数来计算数据段长度这一字段,该参数为传入值
// 返回值:
// void
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
void FrameHandler::setFrameFront(FrameFront& frameFront, const char* data)
{
// 将7Bytes的前导符设置为十六进制的AA-AA-AA-AA-AA-AA-AA
for (int i = 0; i < 7; i++)
{
frameFront.preamble[i] = (unsigned char) 0xAA;
}
// 将帧前定位符设定为十六进制的AB
frameFront.preamble[7] = (unsigned char) 0xAB;
// 设定目的MAC为FF-FF-FF-FF-FF-FF,源MAC为00-16-76-B4-E4-77
unsigned char srcMac[] = {0x00, 0x16, 0x76, 0xB4, 0xE4, 0x77};
for (i = 0; i < 6; i++)
{
frameFront.destMAC[i] = (unsigned char) 0xFF;
frameFront.srcMAC[i] = srcMac[i];
}
// 写入数据长度字段
unsigned short dataLen = strlen(data);
frameFront.dataLength[0] = (unsigned char) ((dataLen >> 8) & 0x00FF);
frameFront.dataLength[1] = (unsigned char) (dataLen & 0x00FF);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 内部辅助函数,该函数在package()中被调用
// 功能:
// 将Ethernet帧保存到由成员变量filePath所指定的文件中
// 参数:
// const FrameFront& frameFront : 前导码、帧前定位符、目的MAC、源MAC、数据长度等字段,该参数为传入值
// const char* data : Ethernet帧的数据段,该参数为传入值
// const unsigned char crc : CRC校验码,根据data字段计算出来的,该参数为传入值
// 返回值:
// 如果成功的将Ethernet帧写入filePath指定的文件中,则返回true
// 如果在写入时发生任何错误,则返回false
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool FrameHandler::storeFrameInFile(const FrameFront& frameFront, const char* data, const unsigned char crc)
{
// 以二进制追加的方式,打开filePath指定的文件
FILE* file = fopen(filePath, "ab");
// 打开文件失败,则输出错误信息,并返回false,表示函数失败
if (file == NULL)
{
cout << "\n无法打开文件 [" << filePath << "] !请确认文件路径是否正确!\n";
return false; // 函数失败返回
}
// 将前导符、帧前定位符、目的MAC、源MAC、数据长度等字段写入文件中
if (1 > fwrite(&frameFront, sizeof(frameFront), 1, file))
{
return false;
}
// 将数据段写入文件中
int dataLen = strlen(data);
if (dataLen > fwrite(data, sizeof(char), dataLen, file))
{
return false;
}
// 如果数据段的长度小于MIN_DATA_LENGTH(即46Bytes),则补齐最小长度
if (dataLen < MIN_DATA_LENGTH)
{
unsigned char* fillBytes = new unsigned char[MIN_DATA_LENGTH - dataLen];
memset(fillBytes, 0, MIN_DATA_LENGTH - dataLen);
if ((MIN_DATA_LENGTH - dataLen) > fwrite(fillBytes, sizeof(unsigned char), MIN_DATA_LENGTH - dataLen, file))
{
return false;
}
}
// 写入CRC校验码
if (1 > fwrite(&crc, sizeof(unsigned char), 1, file))
{
return false;
}
// 至此,一个Ethernet帧已经全部写入文件filePath中,关闭文件并返回true
fclose(file);
return true;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 内部辅助函数,在unpackage()函数中被调用
// 功能:
// 输出前导符、帧前定位符、目的地址、源地址、数据长度等信息
// 参数:
// const FrameFront& frameFront : Ethernet帧的“前面部分”,即前导码、帧前定位符、目的MAC、源MAC、数据长度
// 该参数为传入值
// 返回值:
// void
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void FrameHandler::outputFrameFront(const FrameFront& frameFront)
{
// 输出前导码
cout << setw(WIDTH) << "前导码" << ":";
for (int i = 0; i < 7; i++)
{
cout << hexAlpha[frameFront.preamble[i] >> 4] << hexAlpha[frameFront.preamble[i] & 0x0F] << " ";
}
cout << endl;
// 输出帧前定位符
cout << setw(WIDTH) << "帧前定位符" << ":";
cout << hexAlpha[frameFront.preamble[7] >> 4] << hexAlpha[frameFront.preamble[7] & 0x0F] << endl;
// 输出目的地址
cout << setw(WIDTH) << "目的地址" << ":";
for (i = 0; i < 6; i++)
{
cout << hexAlpha[frameFront.destMAC[i] >> 4] << hexAlpha[frameFront.destMAC[i] & 0x0F];
if (i != 5)
{
cout << '-';
}
}
cout << endl;
// 输出源地址
cout << setw(WIDTH) << "源地址" << ":";
for (i = 0; i < 6; i++)
{
cout << hexAlpha[frameFront.srcMAC[i] >> 4] << hexAlpha[frameFront.srcMAC[i] & 0x0F];
if (i != 5)
{
cout << '-';
}
}
cout << endl;
// 输出长度字段
cout << setw(WIDTH) << "长度字段" << ":";
for (i = 0; i < 2; i++)
{
cout << hexAlpha[frameFront.dataLength[i] >> 4] << hexAlpha[frameFront.dataLength[i] & 0x0F] << " ";
}
cout << endl;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -