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

复制代码

/////////////////////////////

///帧信息类 

/////////////////////////////

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 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<<"没有找到合法的帧"<

                file.close();

                return false;

            }

            // 看当前字符是不是xaa,如果不是,则重新寻找个连续的xaa

            if (file.get() != 0xaa)                

            {

                j = -1;

            }

        }

        if (file.tellg() >= nFileEnd)            // 安全性检测

        {

            cout<<"没有找到合法的帧"<

            file.close();

            return false;

        }

        if (file.get() == 0xab)                    // 判断个连续的xaa之后是否为xab

        {

            break;

        }

    }

    // 将数据字段偏移量定位在上述二进制串之后字节处,并准备进入解析阶段

    nCurrDataOffset = static_cast(file.tellg()) + 14;

    file.seekg(-8,ios::cur);

    // 主控循环

    while ( bParseCont ) // 当仍然可以继续解析输入文件时,继续解析

    {

        // 检测剩余文件是否可能包含完整帧头

        if (static_cast(file.tellg())+ 14 > nFileEnd)// 从目的字段到类型字段总共14字节

        {

            cout<

            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(file.tellg()) - 8 - 1 - nCurrDataOffset) :    // 没到文件末尾:下一帧头位置- 前导码和定界符长度- CRC校验码长度- 数据字段起始位置

        (static_cast(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(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;

    }

}

复制代码



阅读原文

你可能感兴趣的:(C++ Exercises(十六)---Ethernet帧包结构解析)