前言
在网络通讯等数据流处理的时候,往往遇到粘包,或者数据位偏移,本文介绍下如何处理。
提示:以下是本篇文章正文内容,下面案例可供参考
一、数据分析要点
1.缓冲区找到帧头,确定数据长度;然后截取;保留剩余数据
2.重复 1步骤
3.如果需要位怕偏移,需要偏移后进行字节拼装整理;
二、代码实现
.h代码如下(示例):
class CFrameTransChg
{
public :
CFrameTransChg(); //默认构造 1ACFFC1D
CFrameTransChg(unsigned char* Header, int len); //任意帧头处理
void AddBuf(const unsigned char*, unsigned int len);//添加数据
unsigned char* GetBuf(unsigned int& len); //获取解析数据 可以用 While 判断返回值 进行大量数据处理
const static int FramLen = 1024; //截取长度
unsigned int Len() {return m_lenIn; }//当前数据长度
protected:
//按位找头
unsigned char* ResultBitIdx(unsigned char * buf, int len, int & rtLen, int & rtBitidx, int& bFind, int offset);
void ProcData();
private:
unsigned char* m_Trans;
unsigned int m_lenOut;
unsigned char* m_InPut;
unsigned int m_lenIn;
unsigned char* m_header;
unsigned int m_lenHeader;
cpp代码如下(示例):
CFrameTransChg::CFrameTransChg()
{
m_header = new unsigned char[4];
int Head = 0x1DFCCF1A;
memcpy(m_header,&Head, 4);
m_lenHeader = 4;
m_Trans = nullptr;
}
CFrameTransChg::CFrameTransChg(unsigned char * Header, int len)
{
m_header = new unsigned char[len];
m_Trans = nullptr;
memcpy(m_header, Header, len);
}
void CFrameTransChg::AddBuf(const unsigned char * buf, unsigned int len)
{
BYTE* oldDt = m_InPut;
m_InPut = new BYTE[m_lenIn + len];
if(oldDt)
{
memcpy(m_InPut, oldDt, m_lenIn);
delete[]oldDt;
}
memcpy(m_InPut+ m_lenIn, buf, len);
m_lenIn += len;
}
unsigned char * CFrameTransChg::GetBuf(unsigned int & len)
{
ProcData();
if(m_Trans)
{
BYTE* rt = new BYTE[FramLen];
memcpy(rt, m_Trans, FramLen);
delete[]m_Trans;
m_Trans = nullptr;
return rt;
}
return m_Trans;
}
unsigned char * CFrameTransChg::ResultBitIdx(unsigned char * buf, int len, int & rtLen, int & rtBitidx, int& bFind, int offset)
{
BYTE* search= m_header;
int searchCount = 0;
int bitLen = len * sizeof(BYTE) * 8;
int lst = 0; //下一次索引
BYTE* resultBuf = new BYTE[len];
rtLen = 0;
memset(resultBuf, 0, len * sizeof(BYTE));
int offsetIdx = offset % 8;
bFind = 0;
for (int i = offsetIdx; i < bitLen; ) //循环一次
{
int idxByte = i / 8;
int idxBit = i % 8;
int Left = 8 - idxBit;
BYTE Hb = buf[idxByte] << idxBit;
BYTE Lb = 0;
if (idxBit > 0)
{
if (idxByte < len - 1)//modify 20231009
Lb = (buf[idxByte + 1] >> Left);
else
break;
}
BYTE BitIdxNew = Hb | Lb;
if (bFind)//找到了 赋值
{
resultBuf[rtLen++] = BitIdxNew;
i += 8;
if (rtLen == FramLen - m_lenHeader) //足够了 不找了 需要重置最后一个位
{
if(idxByte< (bitLen-1))
buf[idxByte + 1] = (buf[idxByte + 1] << idxBit);
break;
}
continue;
}else
{
if (BitIdxNew == *search)
{
searchCount++;
search++;
lst = i;
if (searchCount == m_lenHeader)//第一次找到
{
rtBitidx = i- m_lenHeader*8; //位地址
bFind = 1;
}
i += 8;
continue;
}
if(searchCount>0)//没用找全Header 重置i索引为往前回滚8个Byte
{
i = lst; //
}
searchCount = 0;
search = m_header;
}
i++;
}
return resultBuf;
}
void CFrameTransChg::ProcData()
{
int rtBitidx = 0;
int rtLen = 0;
int Inpt = m_lenIn > CFrameTransChg::FramLen+4 ? CFrameTransChg::FramLen+4 : m_lenIn; // 2048 理论上选择 CFrameTransChg::FramLen
if (Inpt <= 100)
return;
int bFind;
BYTE* resultBuf = ResultBitIdx(m_InPut, Inpt, rtLen, rtBitidx, bFind, 0);
BYTE* oldDt = m_InPut;
if (rtLen == 0) //保留头长度下次校验
{
if(m_lenIn - Inpt >0)
{
m_InPut = new BYTE[m_lenIn - Inpt];
memcpy(m_InPut, oldDt + Inpt, m_lenIn - Inpt);
delete[]oldDt;
m_lenIn -= Inpt;
}
}
else
{
if (rtLen == FramLen - m_lenHeader)//剩余保留
{
m_InPut = new BYTE[m_lenIn - rtBitidx / 8 - FramLen];
memcpy(m_InPut, oldDt + rtBitidx / 8 + FramLen, m_lenIn - rtBitidx / 8 - FramLen);
delete[]oldDt;
if (m_Trans) delete[]m_Trans;
m_Trans = nullptr;
m_Trans = new BYTE[FramLen];
m_lenOut = FramLen;
memcpy(m_Trans, m_header, m_lenHeader);
memcpy(m_Trans+ m_lenHeader, resultBuf, rtLen);
m_lenIn -= (rtBitidx / 8 +FramLen);
}
else //保留头之前的
{
if(m_lenIn>0)
{
m_InPut = new BYTE[m_lenIn - rtBitidx / 8];
memcpy(m_InPut, oldDt + rtBitidx / 8, m_lenIn - rtBitidx / 8);
delete[]oldDt;
m_lenIn -= (rtBitidx / 8);
}
}
}
delete[]resultBuf;
}
三、测试
帧头1ACFFC1D 定长1K 位偏>>2测试
总结
在数据传输的项目中找帧头,非常普遍,方法仅供初学者参考