C++ Exercises(十六)---Ethernet帧包结构解析

图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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值