图1是一个假想的帧包结构,
 
图2是解包后的结果。

 

  1. /////////////////////////////  
  2. ///帧信息类   
  3. /////////////////////////////  
  4. class CFrame  
  5. {  
  6. public:  
  7.     CFrame(void);  
  8.     ~CFrame(void);  
  9.     void setSerialNumber(int nSN);  
  10.     void setPreCode(const string& strPreCode);  
  11.     void setPreBoundCode(const string& strBoundCode);  
  12.     void setEtherType(const string& strEtherType);  
  13.     void setData(char* strData,int len);  
  14.     void setCRC(const string& strCRC);  
  15.     void setFrameState(bool isValid);  
  16.     void setDstAddress(const string& desAddress);  
  17.     void setSrcAddress(const string& srcAddress);  
  18. private:  
  19.     int nFrameSN;// 帧序号  
  20.     string strPreCode;//前导码  
  21.     string strPreBoundCode;//帧前定界符  
  22.     string strDstAddress;//目的地址  
  23.     string strSrcAddress;//源地址  
  24.     string strEtherType;//帧类型  
  25.     string strData;//数据域  
  26.     string strCRC;//CRC校验码  
  27.     bool bIsValid;//是否正确的帧  
  28.     friend ostream& operator << (ostream& out,const CFrame& frame);  
  29. };  
  30.  
  31. CFrame::CFrame(void)  
  32. {  
  33.     this->nFrameSN = -1;  
  34.     this->bIsValid = false;  
  35.     this->strEtherType = "";  
  36.     this->strCRC = "";  
  37.     this->strData = "";  
  38.     this->strDstAddress = "";  
  39.     this->strPreBoundCode = "";  
  40.     this->strPreCode = "";  
  41.     this->strSrcAddress = "";  
  42. }  
  43.  
  44. CFrame::~CFrame(void)  
  45. {  
  46. }  
  47.  
  48. void CFrame::setSerialNumber(int nSN)  
  49. {  
  50.     this->nFrameSN = nSN;  
  51. }  
  52.  
  53. void CFrame::setPreCode(const string& strPreCode)  
  54. {  
  55.     this->strPreCode = strPreCode;  
  56. }  
  57. void CFrame::setPreBoundCode(const string& strBoundCode)  
  58. {  
  59.     this->strPreBoundCode = strBoundCode;  
  60. }  
  61. void CFrame::setEtherType(const string& strEtherType)  
  62. {  
  63.     this->strEtherType = strEtherType;  
  64. }  
  65. void CFrame::setData(char* strData,int len)  
  66. {  
  67.     this->strData = string(strData,len);  
  68. }  
  69.  
  70. void CFrame::setCRC(const string& strCRC)  
  71. {  
  72.     this->strCRC = strCRC;  
  73. }  
  74.  
  75. void CFrame::setFrameState(bool isValid)  
  76. {  
  77.     this->bIsValid = isValid;  
  78. }  
  79. void CFrame::setDstAddress(const string& desAddress)  
  80. {  
  81.     this->strDstAddress = desAddress;  
  82. }  
  83. void CFrame::setSrcAddress(const string& srcAddress)  
  84. {  
  85.     this->strSrcAddress = srcAddress;  
  86. }  

 

  1.  
  2. /////////////////////////////  
  3. ///帧解析器类  
  4. ///////////////////////////  
  5.  
  6. class CFrameParser  
  7. {  
  8. public:  
  9.     CFrameParser(void);  
  10.     CFrameParser(const char* pFilePath);  
  11.     CFrameParser(const string& strFilePath);  
  12.     ~CFrameParser(void);  
  13.     bool DoParser();//实际的解析动作  
  14. private:  
  15.     string strInputFile;//帧数据文件  
  16.     vector<CFrame> vecFrames;//帧包列表  
  17.       
  18. };  
  19.  
  20.  
  21. CFrameParser::CFrameParser(void)  
  22. {  
  23. }  
  24.  
  25. CFrameParser::~CFrameParser(void)  
  26. {  
  27. }  
  28.  
  29. CFrameParser::CFrameParser(const char* pFilePath):strInputFile(pFilePath)  
  30. {  
  31.  
  32. }  
  33. CFrameParser::CFrameParser(const string& strFilePath):strInputFile(strFilePath)  
  34. {  
  35.  
  36. }  
  37.  
  38. bool CFrameParser::DoParser()  
  39. {    // 检测输入文件是否存在,并可以按所需的权限和方式打开  
  40.     ifstream file(this->strInputFile.c_str(), ios::in|ios::binary|ios::_Nocreate);  
  41.     if (!file.is_open())  
  42.     {  
  43.         cout << "无法打开帧封装包文件,请检查文件是否存在并且未损坏" << endl;  
  44.         return false;  
  45.     }  
  46.       
  47.     // 变量声明及初始化  
  48.     int nSN = 1;                        // 帧序号  
  49.     int nCheck = 0;                        // 校验码  
  50.     int nCurrDataOffset = 22;            // 帧头偏移量  
  51.     int nCurrDataLength = 0;            // 数据字段长度  
  52.     bool bParseCont = true;                // 是否继续对输入文件进行解析  
  53.     int nFileEnd = 0;                    // 输入文件的长度  
  54.     // 计算输入文件的长度  
  55.     file.seekg(0, ios::end);            // 把文件指针移到文件的末尾  
  56.     nFileEnd = file.tellg();            // 取得输入文件的长度  
  57.     file.seekg(0, ios::beg);            // 文件指针位置初始化  
  58.     cout.fill('0');                        // 显示初始化  
  59.     cout.setf(ios::uppercase);            // 以大写字母输出  
  60.     // 定位到输入文件中的第一个有效帧  
  61.     // 从文件头开始,找到第一个连续的“AA-AA-AA-AA-AA-AA-AA-AB”  
  62.     while ( true )  
  63.     {          
  64.         for (int j = 0; j < 7; j++)                // 找个连续的xaa  
  65.         {              
  66.             if (file.tellg() >= nFileEnd)        // 安全性检测  
  67.             {  
  68.                 cout<<"没有找到合法的帧"<<endl;  
  69.                 file.close();  
  70.                 return false;  
  71.             }  
  72.             // 看当前字符是不是xaa,如果不是,则重新寻找个连续的xaa  
  73.             if (file.get() != 0xaa)                  
  74.             {  
  75.                 j = -1;  
  76.             }  
  77.         }  
  78.         if (file.tellg() >= nFileEnd)            // 安全性检测  
  79.         {  
  80.             cout<<"没有找到合法的帧"<<endl;  
  81.             file.close();  
  82.             return false;  
  83.         }  
  84.         if (file.get() == 0xab)                    // 判断个连续的xaa之后是否为xab  
  85.         {  
  86.             break;  
  87.         }  
  88.     }  
  89.     // 将数据字段偏移量定位在上述二进制串之后字节处,并准备进入解析阶段  
  90.     nCurrDataOffset = static_cast<int>(file.tellg()) + 14;  
  91.     file.seekg(-8,ios::cur);  
  92.  
  93.     // 主控循环  
  94.     while ( bParseCont ) // 当仍然可以继续解析输入文件时,继续解析  
  95.     {  
  96.         // 检测剩余文件是否可能包含完整帧头  
  97.         if (static_cast<int>(file.tellg())+ 14 > nFileEnd)// 从目的字段到类型字段总共14字节  
  98.         {  
  99.             cout<<endl<<"没有找到完整帧头,解析终止"<<endl;  
  100.             file.close();  
  101.             return false;  
  102.         }  
  103.         CFrame frame;  
  104.         int c;                        // 读入字节  
  105.         int i = 0;                    // 循环控制变量                      
  106.         int EtherType = 0;            // 由帧中读出的类型字段  
  107.         bool bAccept = true;        // 是否接受该帧  
  108.         // 输出帧的序号  
  109.         frame.setSerialNumber(nSN);  
  110.         // 输出前导码,只输出,不校验  
  111.         string tmpPreCode="";  
  112.         for (i = 0; i <7; i++)                    // 输出格式为:AA AA AA AA AA AA AA  
  113.         {  
  114.             c = file.get();  
  115.             string hexCode = util::ConvertToHex(c);  
  116.             tmpPreCode.append(hexCode);  
  117.             if (i!=6)  
  118.             {  
  119.                 tmpPreCode.append(1,' ');  
  120.             }  
  121.         }  
  122.         frame.setPreCode(tmpPreCode);  
  123.         // 输出帧前定界符,只输出,不校验  
  124.         cout << endl << "帧前定界符:\t";          
  125.         cout.width(2);                            // 输出格式为:AB  
  126.         c = file.get();  
  127.         string tmpBoundCode = util::ConvertToHex(c);  
  128.         frame.setPreBoundCode(tmpBoundCode);  
  129.         string tmpDesAddress;  
  130.         // 输出目的地址,并校验  
  131.         for (i = 1; i <=6; i++)                    // 输出格式为:xx-xx-xx-xx-xx-xx  
  132.         {  
  133.             c = file.get();  
  134.             string desAddr = util::ConvertToHex(c);  
  135.             tmpDesAddress.append(desAddr);  
  136.             if (i!=6)  
  137.             {  
  138.                 tmpDesAddress.append(1,'-');  
  139.             }  
  140.             if (i == 1)                            // 第一个字节,作为“余数”等待下一个bit  
  141.             {  
  142.                 nCheck = c;  
  143.             }  
  144.             else                                // 开始校验  
  145.             {  
  146.                 util::CRC::checkCRC(nCheck, c);  
  147.             }  
  148.         }  
  149.         frame.setDstAddress(tmpDesAddress);  
  150.         string tmpSrcAddress;  
  151.         // 输出源地址,并校验  
  152.         for (i = 1; i <=6; i++)                    // 输出格式为:xx-xx-xx-xx-xx-xx  
  153.         {  
  154.             c = file.get();  
  155.             string srcAddr = util::ConvertToHex(c);  
  156.             tmpSrcAddress.append(srcAddr);  
  157.             if (i!=6)  
  158.             {  
  159.                 tmpSrcAddress.append(1,'-');  
  160.             }  
  161.             util::CRC::checkCRC(nCheck, c);                // 继续校验  
  162.         }  
  163.         frame.setSrcAddress(tmpSrcAddress);  
  164.         //// 输出类型字段,并校验                              
  165.         // 输出类型字段的高位  
  166.         c = file.get();  
  167.         util::CRC::checkCRC(nCheck, c);                    // CRC校验  
  168.         EtherType = c;  
  169.         // 输出类型字段的低位  
  170.         c = file.get();                          
  171.         util::CRC::checkCRC(nCheck,c);                        // CRC校验  
  172.         EtherType <<= 8;                        // 转换成主机格式  
  173.         EtherType |= c;  
  174.         string tmpType = util::ConvertToType(EtherType);  
  175.         frame.setEtherType(tmpType);  
  176.         // 定位下一个帧,以确定当前帧的结束位置  
  177.         while ( bParseCont )  
  178.         {  
  179.             for (int i = 0; i < 7; i++)                    //找下一个连续的个xaa  
  180.             {                  
  181.                 if (file.tellg() >= nFileEnd)            //到文件末尾,退出循环  
  182.                 {  
  183.                     bParseCont = false;  
  184.                     break;  
  185.                 }  
  186.                 // 看当前字符是不是xaa,如果不是,则重新寻找个连续的xaa  
  187.                 if (file.get() != 0xaa)  
  188.                 {  
  189.                     i = -1;  
  190.                 }  
  191.             }  
  192.             // 如果直到文件结束仍没找到上述比特串,将终止主控循环的标记bParseCont置为true  
  193.             bParseCont = bParseCont && (file.tellg() < nFileEnd);          
  194.             // 判断个连续的xaa之后是否为xab  
  195.             if (bParseCont && file.get() == 0xab)          
  196.             {  
  197.                 break;  
  198.             }  
  199.         }  
  200.         // 计算数据字段的长度  
  201.         nCurrDataLength =                                  
  202.             bParseCont ?                                // 是否到达文件末尾  
  203.             (static_cast<int>(file.tellg()) - 8 - 1 - nCurrDataOffset) :    // 没到文件末尾:下一帧头位置- 前导码和定界符长度- CRC校验码长度- 数据字段起始位置  
  204.         (static_cast<int>(file.tellg()) - 1 - nCurrDataOffset);        // 已到达文件末尾:文件末尾位置- CRC校验码长度- 数据字段起始位置  
  205.         // 以文本格式数据字段,并校验  
  206.         char* pData = new char[nCurrDataLength];    // 创建缓冲区  
  207.         file.seekg(bParseCont ? (-8 - 1 -nCurrDataLength) : ( -1 - nCurrDataLength), ios::cur);  
  208.         file.read(pData, nCurrDataLength);                // 读入数据字段  
  209.         frame.setData(pData,nCurrDataLength);  
  210.         int nCount = 50;                                // 每行的基本字符数量  
  211.         for (i = 0; i < nCurrDataLength; i++)            // 输出数据字段文本      
  212.         {  
  213.             util::CRC::checkCRC(nCheck, (int)pData[i]);            // CRC校验  
  214.         }  
  215.         delete[] pData;                                    //释放缓冲区空间  
  216.         // 输出CRC校验码,如果CRC校验有误,则输出正确的CRC校验码  
  217.         cout << endl <<"CRC校验";  
  218.         c = file.get();                                // 读入CRC校验码  
  219.         int nTmpCRC = nCheck;  
  220.         util::CRC::checkCRC(nCheck, c);                        // 最后一步校验  
  221.         string strCRC = util::ConvertToHex(c);  
  222.         frame.setCRC(strCRC);  
  223.         if ((nCheck & 0xff) != 0)                    // CRC校验无误  
  224.         {  
  225.             bAccept = false;                        // 将帧的接收标记置为false  
  226.         }  
  227.         //    如果数据字段长度不足字节或数据字段长度超过字节,则将帧的接收标记置为false      
  228.         if (nCurrDataLength < 46 ||    nCurrDataLength > 1500 )                              
  229.         {  
  230.             bAccept = false;  
  231.         }  
  232.         frame.setFrameState(bAccept);  
  233.         vecFrames.push_back(frame);  
  234.         nSN++;                                    // 帧序号加  
  235.         nCurrDataOffset = static_cast<int>(file.tellg()) + 22;    // 将数据字段偏移量更新为下一帧的帧头结束位置  
  236.     }  
  237.     // 关闭输入文件  
  238.     file.close();  
  239.     return true;  

 

  1. namespace util  
  2. {//实用工具  
  3.     class CRC  
  4.     {  
  5.     public:  
  6.     ////////////////////////////////////////////////////////////////////////////////  
  7.         // CRC校验,在上一轮校验的基础上继续作位CRC校验  
  8.         //   
  9.         //    输入参数:  
  10.         //        chCurrByte    低位数据有效,记录了上一次CRC校验的余数  
  11.         //        chNextByte    低位数据有效,记录了本次要继续校验的一个字节  
  12.         //  
  13.         //    传出参数:  
  14.         //        chCurrByte    低位数据有效,记录了本次CRC校验的余数  
  15.         ////////////////////////////////////////////////////////////////////////////////  
  16.         static void checkCRC(int &chCurrByte, int chNextByte)  
  17.         {  
  18.             // CRC循环:每次调用进行次循环,处理一个字节的数据。  
  19.             for (int nMask = 0x80; nMask > 0; nMask >>= 1)  
  20.             {  
  21.                 if ((chCurrByte & 0x80) != 0)        // 首位为1:移位,并进行异或运算            {      
  22.                     chCurrByte <<= 1;                // 移一位  
  23.                     if ( (chNextByte & nMask) != 0)    // 补一位  
  24.                     {  
  25.                         chCurrByte |= 1;  
  26.                     }  
  27.                     chCurrByte ^= 7;                // 首位已经移出,仅对低位进行异或运算,的二进制为,0111  
  28.                 }  
  29.                 else                                // 首位为0,只移位,不进行异或运算  
  30.                 {          
  31.                     chCurrByte <<= 1;                // 移一位  
  32.                     if ( (chNextByte & nMask) != 0)    // 补一位  
  33.                     {  
  34.                         chCurrByte |= 1;  
  35.                     }  
  36.                 }  
  37.             }  
  38.         }  
  39.     };  
  40.     char mappingTable[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};  
  41.     string ConvertToHex(int ch)  
  42.     {  
  43.         int high = ch/16;  
  44.         int low = ch%16;  
  45.         string result;  
  46.         result.append(1,mappingTable[high]);  
  47.         result.append(1,mappingTable[low]);  
  48.         return result;  
  49.     }  
  50.     string ConvertToType(int ch)  
  51.     {  
  52.         string result;  
  53.         int num,i;  
  54.         for (i=0;i<4;++i)  
  55.         {  
  56.             num = ch & 0x000F;  
  57.             ch>>=4;  
  58.             result.append(1,mappingTable[num]);  
  59.             if (i==1)  
  60.             {  
  61.                 result.append(1,' ');  
  62.             }  
  63.         }  
  64.           
  65.         for (i=0;i<=1;++i)  
  66.         {  
  67.             swap(result[i],result[4-i]);  
  68.         }  
  69.         return result;  
  70.     }