图1是一个假想的帧包结构,
图2是解包后的结果。
- /////////////////////////////
- ///帧信息类
- /////////////////////////////
- class CFrame
- {
- public:
- CFrame(void);
- ~CFrame(void);
- void setSerialNumber(int nSN);
- void setPreCode(const string& strPreCode);
- void setPreBoundCode(const string& strBoundCode);
- void setEtherType(const string& strEtherType);
- void setData(char* strData,int len);
- void setCRC(const string& strCRC);
- void setFrameState(bool isValid);
- void setDstAddress(const string& desAddress);
- void setSrcAddress(const string& srcAddress);
- private:
- int nFrameSN;// 帧序号
- string strPreCode;//前导码
- string strPreBoundCode;//帧前定界符
- string strDstAddress;//目的地址
- string strSrcAddress;//源地址
- string strEtherType;//帧类型
- string strData;//数据域
- string strCRC;//CRC校验码
- bool bIsValid;//是否正确的帧
- friend ostream& operator << (ostream& out,const CFrame& frame);
- };
- CFrame::CFrame(void)
- {
- this->nFrameSN = -1;
- this->bIsValid = false;
- this->strEtherType = "";
- this->strCRC = "";
- this->strData = "";
- this->strDstAddress = "";
- this->strPreBoundCode = "";
- this->strPreCode = "";
- this->strSrcAddress = "";
- }
- CFrame::~CFrame(void)
- {
- }
- void CFrame::setSerialNumber(int nSN)
- {
- this->nFrameSN = nSN;
- }
- void CFrame::setPreCode(const string& strPreCode)
- {
- this->strPreCode = strPreCode;
- }
- void CFrame::setPreBoundCode(const string& strBoundCode)
- {
- this->strPreBoundCode = strBoundCode;
- }
- void CFrame::setEtherType(const string& strEtherType)
- {
- this->strEtherType = strEtherType;
- }
- void CFrame::setData(char* strData,int len)
- {
- this->strData = string(strData,len);
- }
- void CFrame::setCRC(const string& strCRC)
- {
- this->strCRC = strCRC;
- }
- void CFrame::setFrameState(bool isValid)
- {
- this->bIsValid = isValid;
- }
- void CFrame::setDstAddress(const string& desAddress)
- {
- this->strDstAddress = desAddress;
- }
- void CFrame::setSrcAddress(const string& srcAddress)
- {
- this->strSrcAddress = srcAddress;
- }
- /////////////////////////////
- ///帧解析器类
- ///////////////////////////
- class CFrameParser
- {
- public:
- CFrameParser(void);
- CFrameParser(const char* pFilePath);
- CFrameParser(const string& strFilePath);
- ~CFrameParser(void);
- bool DoParser();//实际的解析动作
- private:
- string strInputFile;//帧数据文件
- vector<CFrame> vecFrames;//帧包列表
- };
- CFrameParser::CFrameParser(void)
- {
- }
- CFrameParser::~CFrameParser(void)
- {
- }
- CFrameParser::CFrameParser(const char* pFilePath):strInputFile(pFilePath)
- {
- }
- CFrameParser::CFrameParser(const string& strFilePath):strInputFile(strFilePath)
- {
- }
- bool CFrameParser::DoParser()
- { // 检测输入文件是否存在,并可以按所需的权限和方式打开
- ifstream file(this->strInputFile.c_str(), ios::in|ios::binary|ios::_Nocreate);
- if (!file.is_open())
- {
- cout << "无法打开帧封装包文件,请检查文件是否存在并且未损坏" << endl;
- return false;
- }
- // 变量声明及初始化
- int nSN = 1; // 帧序号
- int nCheck = 0; // 校验码
- int nCurrDataOffset = 22; // 帧头偏移量
- int nCurrDataLength = 0; // 数据字段长度
- bool bParseCont = true; // 是否继续对输入文件进行解析
- int nFileEnd = 0; // 输入文件的长度
- // 计算输入文件的长度
- file.seekg(0, ios::end); // 把文件指针移到文件的末尾
- nFileEnd = file.tellg(); // 取得输入文件的长度
- file.seekg(0, ios::beg); // 文件指针位置初始化
- cout.fill('0'); // 显示初始化
- cout.setf(ios::uppercase); // 以大写字母输出
- // 定位到输入文件中的第一个有效帧
- // 从文件头开始,找到第一个连续的“AA-AA-AA-AA-AA-AA-AA-AB”
- while ( true )
- {
- for (int j = 0; j < 7; j++) // 找个连续的xaa
- {
- if (file.tellg() >= nFileEnd) // 安全性检测
- {
- cout<<"没有找到合法的帧"<<endl;
- file.close();
- return false;
- }
- // 看当前字符是不是xaa,如果不是,则重新寻找个连续的xaa
- if (file.get() != 0xaa)
- {
- j = -1;
- }
- }
- if (file.tellg() >= nFileEnd) // 安全性检测
- {
- cout<<"没有找到合法的帧"<<endl;
- file.close();
- return false;
- }
- if (file.get() == 0xab) // 判断个连续的xaa之后是否为xab
- {
- break;
- }
- }
- // 将数据字段偏移量定位在上述二进制串之后字节处,并准备进入解析阶段
- nCurrDataOffset = static_cast<int>(file.tellg()) + 14;
- file.seekg(-8,ios::cur);
- // 主控循环
- while ( bParseCont ) // 当仍然可以继续解析输入文件时,继续解析
- {
- // 检测剩余文件是否可能包含完整帧头
- if (static_cast<int>(file.tellg())+ 14 > nFileEnd)// 从目的字段到类型字段总共14字节
- {
- cout<<endl<<"没有找到完整帧头,解析终止"<<endl;
- file.close();
- return false;
- }
- CFrame frame;
- int c; // 读入字节
- int i = 0; // 循环控制变量
- int EtherType = 0; // 由帧中读出的类型字段
- bool bAccept = true; // 是否接受该帧
- // 输出帧的序号
- frame.setSerialNumber(nSN);
- // 输出前导码,只输出,不校验
- string tmpPreCode="";
- for (i = 0; i <7; i++) // 输出格式为:AA AA AA AA AA AA AA
- {
- c = file.get();
- string hexCode = util::ConvertToHex(c);
- tmpPreCode.append(hexCode);
- if (i!=6)
- {
- tmpPreCode.append(1,' ');
- }
- }
- frame.setPreCode(tmpPreCode);
- // 输出帧前定界符,只输出,不校验
- cout << endl << "帧前定界符:\t";
- cout.width(2); // 输出格式为:AB
- c = file.get();
- string tmpBoundCode = util::ConvertToHex(c);
- frame.setPreBoundCode(tmpBoundCode);
- string tmpDesAddress;
- // 输出目的地址,并校验
- for (i = 1; i <=6; i++) // 输出格式为:xx-xx-xx-xx-xx-xx
- {
- c = file.get();
- string desAddr = util::ConvertToHex(c);
- tmpDesAddress.append(desAddr);
- if (i!=6)
- {
- tmpDesAddress.append(1,'-');
- }
- if (i == 1) // 第一个字节,作为“余数”等待下一个bit
- {
- nCheck = c;
- }
- else // 开始校验
- {
- util::CRC::checkCRC(nCheck, c);
- }
- }
- frame.setDstAddress(tmpDesAddress);
- string tmpSrcAddress;
- // 输出源地址,并校验
- for (i = 1; i <=6; i++) // 输出格式为:xx-xx-xx-xx-xx-xx
- {
- c = file.get();
- string srcAddr = util::ConvertToHex(c);
- tmpSrcAddress.append(srcAddr);
- if (i!=6)
- {
- tmpSrcAddress.append(1,'-');
- }
- util::CRC::checkCRC(nCheck, c); // 继续校验
- }
- frame.setSrcAddress(tmpSrcAddress);
- //// 输出类型字段,并校验
- // 输出类型字段的高位
- c = file.get();
- util::CRC::checkCRC(nCheck, c); // CRC校验
- EtherType = c;
- // 输出类型字段的低位
- c = file.get();
- util::CRC::checkCRC(nCheck,c); // CRC校验
- EtherType <<= 8; // 转换成主机格式
- EtherType |= c;
- string tmpType = util::ConvertToType(EtherType);
- frame.setEtherType(tmpType);
- // 定位下一个帧,以确定当前帧的结束位置
- while ( bParseCont )
- {
- for (int i = 0; i < 7; i++) //找下一个连续的个xaa
- {
- if (file.tellg() >= nFileEnd) //到文件末尾,退出循环
- {
- bParseCont = false;
- break;
- }
- // 看当前字符是不是xaa,如果不是,则重新寻找个连续的xaa
- if (file.get() != 0xaa)
- {
- i = -1;
- }
- }
- // 如果直到文件结束仍没找到上述比特串,将终止主控循环的标记bParseCont置为true
- bParseCont = bParseCont && (file.tellg() < nFileEnd);
- // 判断个连续的xaa之后是否为xab
- if (bParseCont && file.get() == 0xab)
- {
- break;
- }
- }
- // 计算数据字段的长度
- nCurrDataLength =
- bParseCont ? // 是否到达文件末尾
- (static_cast<int>(file.tellg()) - 8 - 1 - nCurrDataOffset) : // 没到文件末尾:下一帧头位置- 前导码和定界符长度- CRC校验码长度- 数据字段起始位置
- (static_cast<int>(file.tellg()) - 1 - nCurrDataOffset); // 已到达文件末尾:文件末尾位置- CRC校验码长度- 数据字段起始位置
- // 以文本格式数据字段,并校验
- char* pData = new char[nCurrDataLength]; // 创建缓冲区
- file.seekg(bParseCont ? (-8 - 1 -nCurrDataLength) : ( -1 - nCurrDataLength), ios::cur);
- file.read(pData, nCurrDataLength); // 读入数据字段
- frame.setData(pData,nCurrDataLength);
- int nCount = 50; // 每行的基本字符数量
- for (i = 0; i < nCurrDataLength; i++) // 输出数据字段文本
- {
- util::CRC::checkCRC(nCheck, (int)pData[i]); // CRC校验
- }
- delete[] pData; //释放缓冲区空间
- // 输出CRC校验码,如果CRC校验有误,则输出正确的CRC校验码
- cout << endl <<"CRC校验";
- c = file.get(); // 读入CRC校验码
- int nTmpCRC = nCheck;
- util::CRC::checkCRC(nCheck, c); // 最后一步校验
- string strCRC = util::ConvertToHex(c);
- frame.setCRC(strCRC);
- if ((nCheck & 0xff) != 0) // CRC校验无误
- {
- bAccept = false; // 将帧的接收标记置为false
- }
- // 如果数据字段长度不足字节或数据字段长度超过字节,则将帧的接收标记置为false
- if (nCurrDataLength < 46 || nCurrDataLength > 1500 )
- {
- bAccept = false;
- }
- frame.setFrameState(bAccept);
- vecFrames.push_back(frame);
- nSN++; // 帧序号加
- nCurrDataOffset = static_cast<int>(file.tellg()) + 22; // 将数据字段偏移量更新为下一帧的帧头结束位置
- }
- // 关闭输入文件
- file.close();
- return true;
- }
- namespace util
- {//实用工具
- class CRC
- {
- public:
- ////////////////////////////////////////////////////////////////////////////////
- // CRC校验,在上一轮校验的基础上继续作位CRC校验
- //
- // 输入参数:
- // chCurrByte 低位数据有效,记录了上一次CRC校验的余数
- // chNextByte 低位数据有效,记录了本次要继续校验的一个字节
- //
- // 传出参数:
- // chCurrByte 低位数据有效,记录了本次CRC校验的余数
- ////////////////////////////////////////////////////////////////////////////////
- static void checkCRC(int &chCurrByte, int chNextByte)
- {
- // CRC循环:每次调用进行次循环,处理一个字节的数据。
- for (int nMask = 0x80; nMask > 0; nMask >>= 1)
- {
- if ((chCurrByte & 0x80) != 0) // 首位为1:移位,并进行异或运算 {
- chCurrByte <<= 1; // 移一位
- if ( (chNextByte & nMask) != 0) // 补一位
- {
- chCurrByte |= 1;
- }
- chCurrByte ^= 7; // 首位已经移出,仅对低位进行异或运算,的二进制为,0111
- }
- else // 首位为0,只移位,不进行异或运算
- {
- chCurrByte <<= 1; // 移一位
- if ( (chNextByte & nMask) != 0) // 补一位
- {
- chCurrByte |= 1;
- }
- }
- }
- }
- };
- char mappingTable[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
- string ConvertToHex(int ch)
- {
- int high = ch/16;
- int low = ch%16;
- string result;
- result.append(1,mappingTable[high]);
- result.append(1,mappingTable[low]);
- return result;
- }
- string ConvertToType(int ch)
- {
- string result;
- int num,i;
- for (i=0;i<4;++i)
- {
- num = ch & 0x000F;
- ch>>=4;
- result.append(1,mappingTable[num]);
- if (i==1)
- {
- result.append(1,' ');
- }
- }
- for (i=0;i<=1;++i)
- {
- swap(result[i],result[4-i]);
- }
- return result;
- }
- }