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

来源:互联网 发布:反心灵鸡汤的数据 编辑:程序博客网 时间:2024/05/16 15:23

图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;
    }
}
原创粉丝点击