图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;
}
}
复制代码
本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2009/01/03/1367516.html,如需转载请自行联系原作者