主要介绍一下解析网络协议常用的技巧
- 字符串直接转化为16进制字节
例如将"0102FFAB
"的字符串直接转化为:0102FFAB
的16进制字符。
void QtWidgetsApplicationexample::string_to_Hex(QString& textContent, QByteArray& hex)
{
QString _Space = " "; //如果存在空格则去除空格
int _size = textContent.length();
while (_size - 2 > 0)
{
_size = _size - 2;
textContent.insert(_size, " ");
}
textContent = textContent.trimmed();
QStringList _textContentList = textContent.split(" ");
for each (QString m_textContent in _textContentList)
{
if (!m_textContent.isEmpty())
{
hex.append((char)m_textContent.toInt(0, 16) & 0XFF);
}
}
}
QByteArray _hex;
QString _textContent = "0102FFAB";
string_to_Hex(_textContent, _hex);
qDebug() << _hex;
打印出:0102FFAB。
- 16进制字节直接转化为可展示的字符串
例如将0102FFAB
的16进制字符直接转化为:“0102FFAB
”的字符串。
void QtWidgetsApplicationexample::Hex_to_string(QByteArray& hex, QString& textContent)
{
for (char byte : hex)
{
textContent.append(QString::number(static_cast<unsigned char>(byte), 16).rightJustified(2, '0').toUpper());
}
int _size = textContent.size();
while (_size > 0)
{
_size = _size - 2;
textContent.insert(_size, " ");
}
}
QByteArray _hex = QByteArray::fromHex("0102FFAB");
QString _textContent;
Hex_to_string(_hex, _textContent);
qDebug() << _textContent;
打印出:" 01 02 FF AB"
- 解析网络协议
假设,存在一个长度为19字节的长度的一直在刷新的网络消息需要读取解析,协议的帧头帧尾为固定值。前两位帧头为:AAAB,最后两位帧尾为BABB。(倒数第三位是0-15字节的CRC8校验值,关于CRC校验,可以阅读以前的文章)。
- 首先判断收到的数据长度,是否大于等于19
- 如果长度符合则判断帧头帧尾和校验位,全部正确则说明此数据为有效数据。
- 最后解析字节的每一位,得到想要的数据。
以上是主要思路,然后进行实现
//1:使用递归按位寻找帧头
//2:然后截取获取帧尾 和 检验码
//3:校验,对比检验码
//4:获取需要的值
if (!findFrame_Header(data))
return;
if (!findFrame_ender(data))
return;
if (!findFrame_crc8(data))
return;
if (!findFrame_DataArea(data))
return;
findFrame_Hexder(QByteArray& data)
的实现:
const quint16 Frame_Header = 0XAAAB;//帧头为固定数据
bool ReciveMessageFromDoor::findFrame_Header(QByteArray& data)
{
if (data.size() >= 19) //判断长度是否符合
{
QDataStream inStream(data);//从data中读取数据
inStream.setByteOrder(QDataStream::BigEndian);
if (comparsion_Frame_Header(inStream))
{
qDebug() << u8"Find Frame header";
return true;
}
else
{
data.remove(0, 1);
if (findFrame_Header(data))
{
return true;
}
}
}
qDebug() << u8"Not Find Frame header";
return false;
}
bool ReciveMessageFromDoor::comparsion_Frame_Header(QDataStream& data)
{
quint16 _frame_header = 0X0000;
data >> _frame_header;//将前两位数据读取到_frame_header中
if (_frame_header == Frame_Header)
{
return true;
}
return false;
}
程序使用递归的方式判断当前数据是否满足使用。
findFrame_ender(QByteArray& data)
的实现:
bool ReciveMessageFromDoor::findFrame_ender(QByteArray& data)
{
//获取到帧头后,向后取第:17,18位是帧尾
QByteArray _data = data.mid(17, 2);//从下标为17位开始,取两位数据
QDataStream inStream(_data);
inStream.setByteOrder(QDataStream::BigEndian);
if (comparsion_Frame_Ender(inStream))
{
qDebug() << u8"Find Frame Ender";
return true;
}
qDebug() << u8"Not Find Frame Ender";
return false;
}
bool ReciveMessageFromDoor::comparsion_Frame_Ender(QDataStream& data)
{
quint16 _frame_ender = 0X0000;
data >> _frame_ender;
if (_frame_ender == Frame_End)
{
return true;
}
return false;
}
findFrame_crc8(QByteArray& data)
的实现:
bool ReciveMessageFromDoor::findFrame_crc8(QByteArray& data)
{
//获取到帧头后,向后取第:16位是校验码
//对0-15位进行CRC8校验,校验结果等于校验码
QByteArray _dataCRC8 = data.mid(16, 1);
quint8 _inCRC8 = _dataCRC8.at(0);
QByteArray _dataArea = data.mid(0, 16);
quint8 _CRC8 = crc8(_dataArea);
if (_CRC8 == _inCRC8)
{
qDebug() << u8"CRC Frame right";
return true;
}
qDebug() << u8"CRC Frame wrong";
return false;
}
findFrame_DataArea(QByteArray& data)
的实现,假设取第7个字节的第一位数据和第二位数据,判断是0还是1:
bool ReciveMessageFromDoor::findFrame_DataArea(QByteArray& data)
{
//7
QByteArray _byteSeven = data.mid(7, 1);
getBitSeven(_byteSeven);
}
void ReciveMessageFromDoor::getBitSeven(QByteArray& data)
{
int _Zero_Speed = getBytebitIndex(data, 0);
qDebug() << _Zero_Speed;
int _Enabled = getBytebitIndex(data, 1);
qDebug() << _Enabled;
}
4.组装网络字节协议
如上图所示,下列代码演示如何组装19位长度的网络消息:
- 前四位0,1,2,3为固定消息,第5位发递增的生命信号,6,7位为固定消息,8,9,10,11,12是时间,14,15,16是预留字节,17是CRC校验,18,19为固定消息。
const quint8 Frame_Header_One = 0XAA;
const quint8 Frame_Header_Two = 0XAB;
const quint8 Frame_End_One = 0XBA;
const quint8 Frame_End_Two = 0XBB;
const quint8 Door_Timing = 0X02;
QByteArray packageData;
bool TimeMessageToDoor::packagePollingMessage(QByteArray& packageData)
{
packageData.append(Frame_Header_One);
packageData.append(Frame_Header_Two);
packageDoorAddressComplement(packageData);
packageLifeSignal(packageData);
packageData.append(1, char(0));
packageData.append(Door_Timing);
packageCurrentTime(packageData);
packageData.append(3, char(0));//补充三个为0的字节
quint8 _crc = crc8(packageData);
packageData.append(_crc);//省略
packageData.append(Frame_End_One);
packageData.append(Frame_End_Two);
return true;
}
bool TimeMessageToDoor::packageDoorAddressComplement(QByteArray& inStream)
{
if (b_useCycle && m_cyclemethod < 4)
{
m_cyclemethod = m_cyclemethod + 1;
}
else
{
m_cyclemethod = m_cyclemethod;
}
switch (m_cyclemethod)
{
case 1:
inStream.append(Door_One_Address);
inStream.append(Door_One_Address_Complement);
break;
case 2:
inStream.append(Door_Two_Address);
inStream.append(Door_Two_Address_Complement);
break;
case 3:
inStream.append(Door_Three_Address);
inStream.append(Door_Three_Address_Complement);
break;
case 4:
inStream.append(Door_Four_Address);
inStream.append(Door_Four_Address_Complement);
break;
default:
break;
}
return true;
}
bool TimeMessageToDoor::packageLifeSignal(QByteArray& inStream)
{
m_curMode = m_curMode + 1;
if (m_curMode > 255)
{
m_curMode = 1;
}
quint8 mode = (quint8)m_curMode;
inStream.append(mode);
return true;
}
bool TimeMessageToDoor::packageCurrentTime(QByteArray& inStream)
{
QDateTime _dataTime = QDateTime::currentDateTime();
QString _strYMD = _dataTime.toString("yyyy-MM-dd");
QString _strHMS = _dataTime.toString("hh:mm:ss");
//时间采用BCD格式
QString _strYear = _strYMD.split("-")[0];
int _intYear = decimal_bcd_code(_strYear.toInt() - 2000);
QString _strMonth = _strYMD.split("-")[1];
int _intMonth = decimal_bcd_code(_strMonth.toInt());
QString _strDay = _strYMD.split("-")[2];
int _intDay = decimal_bcd_code(_strDay.toInt());
QString _strHour = _strHMS.split(":")[0];
int _intHour = decimal_bcd_code(_strHour.toInt());
QString _strMinute = _strHMS.split(":")[1];
int _intMinute = decimal_bcd_code(_strMinute.toInt());
QString _strSecond = _strHMS.split(":")[1];
int _intSecond = decimal_bcd_code(_strSecond.toInt());
inStream.append(quint8(_intYear));
inStream.append(quint8(_intMonth));
inStream.append(quint8(_intDay));
inStream.append(quint8(_intHour));
inStream.append(quint8(_intMinute));
inStream.append(quint8(_intSecond));
return true;
}