图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;
}
}