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

本文介绍了一种帧数据解析器的设计方案,包括帧信息类和帧解析器类的具体实现细节。通过对帧数据进行逐字节解析,实现了帧的正确识别与验证。

图1是一个假想的帧包结构,

图2是解包后的结果。

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->/////////////////////////////
///帧信息类
/////////////////////////////
classCFrame
{
public:
CFrame(
void);
~CFrame(void);
voidsetSerialNumber(intnSN);
voidsetPreCode(conststring&strPreCode);
voidsetPreBoundCode(conststring&strBoundCode);
voidsetEtherType(conststring&strEtherType);
voidsetData(char*strData,intlen);
voidsetCRC(conststring&strCRC);
voidsetFrameState(boolisValid);
voidsetDstAddress(conststring&desAddress);
voidsetSrcAddress(conststring&srcAddress);
private:
intnFrameSN;//帧序号
stringstrPreCode;//前导码
stringstrPreBoundCode;//帧前定界符
stringstrDstAddress;//目的地址
stringstrSrcAddress;//源地址
stringstrEtherType;//帧类型
stringstrData;//数据域
stringstrCRC;//CRC校验码
boolbIsValid;//是否正确的帧
friendostream&operator<<(ostream&out,constCFrame&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)
{
}

voidCFrame::setSerialNumber(intnSN)
{
this->nFrameSN=nSN;
}

voidCFrame::setPreCode(conststring&strPreCode)
{
this->strPreCode=strPreCode;
}
voidCFrame::setPreBoundCode(conststring&strBoundCode)
{
this->strPreBoundCode=strBoundCode;
}
voidCFrame::setEtherType(conststring&strEtherType)
{
this->strEtherType=strEtherType;
}
voidCFrame::setData(char*strData,intlen)
{
this->strData=string(strData,len);
}

voidCFrame::setCRC(conststring&strCRC)
{
this->strCRC=strCRC;
}

voidCFrame::setFrameState(boolisValid)
{
this->bIsValid=isValid;
}
voidCFrame::setDstAddress(conststring&desAddress)
{
this->strDstAddress=desAddress;
}
voidCFrame::setSrcAddress(conststring&srcAddress)
{
this->strSrcAddress=srcAddress;
}

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->
/////////////////////////////
///帧解析器类
///////////////////////////

classCFrameParser
{
public:
CFrameParser(
void);
CFrameParser(
constchar*pFilePath);
CFrameParser(
conststring&strFilePath);
~CFrameParser(void);
boolDoParser();//实际的解析动作
private:
stringstrInputFile;//帧数据文件
vector<CFrame>vecFrames;//帧包列表

};


CFrameParser::CFrameParser(
void)
{
}

CFrameParser::
~CFrameParser(void)
{
}

CFrameParser::CFrameParser(
constchar*pFilePath):strInputFile(pFilePath)
{

}
CFrameParser::CFrameParser(
conststring&strFilePath):strInputFile(strFilePath)
{

}

boolCFrameParser::DoParser()
{
//检测输入文件是否存在,并可以按所需的权限和方式打开
ifstreamfile(this->strInputFile.c_str(),ios::in|ios::binary|ios::_Nocreate);
if(!file.is_open())
{
cout
<<"无法打开帧封装包文件,请检查文件是否存在并且未损坏"<<endl;
returnfalse;
}

//变量声明及初始化
intnSN=1;//帧序号
intnCheck=0;//校验码
intnCurrDataOffset=22;//帧头偏移量
intnCurrDataLength=0;//数据字段长度
boolbParseCont=true;//是否继续对输入文件进行解析
intnFileEnd=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(intj=0;j<7;j++)//找个连续的xaa
{
if(file.tellg()>=nFileEnd)//安全性检测
{
cout
<<"没有找到合法的帧"<<endl;
file.close();
returnfalse;
}
//看当前字符是不是xaa,如果不是,则重新寻找个连续的xaa
if(file.get()!=0xaa)
{
j
=-1;
}
}
if(file.tellg()>=nFileEnd)//安全性检测
{
cout
<<"没有找到合法的帧"<<endl;
file.close();
returnfalse;
}
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();
returnfalse;
}
CFrameframe;
intc;//读入字节
inti=0;//循环控制变量
intEtherType=0;//由帧中读出的类型字段
boolbAccept=true;//是否接受该帧
//输出帧的序号
frame.setSerialNumber(nSN);
//输出前导码,只输出,不校验
stringtmpPreCode="";
for(i=0;i<7;i++)//输出格式为:AAAAAAAAAAAAAA
{
c
=file.get();
stringhexCode=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();
stringtmpBoundCode=util::ConvertToHex(c);
frame.setPreBoundCode(tmpBoundCode);
stringtmpDesAddress;
//输出目的地址,并校验
for(i=1;i<=6;i++)//输出格式为:xx-xx-xx-xx-xx-xx
{
c
=file.get();
stringdesAddr=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);
stringtmpSrcAddress;
//输出源地址,并校验
for(i=1;i<=6;i++)//输出格式为:xx-xx-xx-xx-xx-xx
{
c
=file.get();
stringsrcAddr=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;
stringtmpType=util::ConvertToType(EtherType);
frame.setEtherType(tmpType);
//定位下一个帧,以确定当前帧的结束位置
while(bParseCont)
{
for(inti=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=newchar[nCurrDataLength];//创建缓冲区
file.seekg(bParseCont?(-8-1-nCurrDataLength):(-1-nCurrDataLength),ios::cur);
file.read(pData,nCurrDataLength);
//读入数据字段
frame.setData(pData,nCurrDataLength);
intnCount=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校验码
intnTmpCRC=nCheck;
util::CRC::checkCRC(nCheck,c);
//最后一步校验
stringstrCRC=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();
returntrue;
}

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->namespaceutil
{
//实用工具
classCRC
{
public:
////////////////////////////////////////////////////////////////////////////////
//CRC校验,在上一轮校验的基础上继续作位CRC校验
//
//输入参数:
//chCurrByte低位数据有效,记录了上一次CRC校验的余数
//chNextByte低位数据有效,记录了本次要继续校验的一个字节
//
//传出参数:
//chCurrByte低位数据有效,记录了本次CRC校验的余数
////////////////////////////////////////////////////////////////////////////////
staticvoidcheckCRC(int&chCurrByte,intchNextByte)
{
//CRC循环:每次调用进行次循环,处理一个字节的数据。
for(intnMask=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;
}
}
}
}
};
charmappingTable[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
stringConvertToHex(intch)
{
inthigh=ch/16;
intlow=ch%16;
stringresult;
result.append(
1,mappingTable[high]);
result.append(
1,mappingTable[low]);
returnresult;
}
stringConvertToType(intch)
{
stringresult;
intnum,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]);
}
returnresult;
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值