Qt---处理粘包_qt数据粘包,纯干货

57 阅读5分钟
char *d1 = "data1";
char *d2 = "data2";
char *d3 = "data3";

NetPacket p1, p2, p3;

p1.len = sizeof("data1");     //封装第一个数据包
memcpy(p1.data, d1, p1.len);

p2.len = sizeof("data2");
memcpy(p2.data, d2, p2.len);

p3.len = sizeof("data3");
memcpy(p3.data, d3, p3.len);

clientConnection->write((char *)&p1, sizeof(int) + p1.len);  //发送数据包
clientConnection->write((char *)&p2, sizeof(int) + p2.len);

clientConnection->write((char *)&p3, sizeof(int) + p3.len); }


客户端



void myTcpClient::slotRead() { while(tcpSocket->bytesAvailable()>0) {

    int len;
    char buf[1024];    //接收数据的缓冲区
    char tmpBuf[1024]; //存放包体
    int nOffset = 0;   //偏移

    int n = tcpSocket->bytesAvailable(); //接收到的字节数
    tcpSocket->read(buf, n);             

    memcpy(&len, buf, sizeof(int));    //包头:包体长度
    nOffset += sizeof(int);  
    memcpy(tmpBuf, buf+nOffset, len);  //包体
    nOffset += len;
    printf("%s\n", tmpBuf);            //打印包体

    memcpy(&len, buf, sizeof(int));
    nOffset += sizeof(int);
    memcpy(tmpBuf, buf+nOffset, len);
    nOffset += len;
    printf("%s\n", tmpBuf);

    memcpy(&len, buf, sizeof(int));
    nOffset += sizeof(int);
    memcpy(tmpBuf, buf+nOffset, len);
    nOffset += len;
    printf("%s\n", tmpBuf);

}

}


运行结果:  
 ![这里写图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/d5c719350460407b8fd2b894d228bb56~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1772460896&x-signature=0ocJrbvLcMTWBQcULKAID8ZJj3Y%3D)


### 完整版


**服务器端和客户端共有的文件**


databuffer.h



#ifndef NETDATABUFFER_H #define NETDATABUFFER_H

#define BUFFER_SIZE 1024 //初始缓冲区大小

class DataBuffer { public: char *m_pBuffer; //缓冲区 int m_nBufferSize; //缓冲区大小 int m_nOffset; //缓冲区中当前数据大小

int getDataLen();         //获得缓冲区中数据大小
bool reBufferSize(int nLen); //调整缓冲区大小
bool addMsg(char *pBuf, int nLen);  //添加消息到缓冲区
void reset();          //缓冲区复位
void poll(int nLen);   //移除缓冲区首部的第一个数据包

public: DataBuffer(); ~DataBuffer(); };

#endif // NETDATABUFFER_H


databuffer.cpp



#include "databuffer.h" #include "string.h" #include

//构造 DataBuffer::DataBuffer() { m_nBufferSize = BUFFER_SIZE; //缓冲区大小 m_nOffset = 0; //缓冲区当前现有数据大小 m_pBuffer = new char[m_nBufferSize]; //分配缓冲区 memset(m_pBuffer, 0, sizeof(m_pBuffer)); //清空缓冲区 }

//析构 DataBuffer::~DataBuffer() { delete [] m_pBuffer; //释放缓冲区 m_pBuffer = NULL; m_nBufferSize = 0; m_nOffset = 0; }

//获得缓冲区中数据大小 int DataBuffer::getDataLen() { return m_nOffset; }

//重置缓冲区大小 bool DataBuffer::reBufferSize(int nLen) { char *oBuffer = m_pBuffer; //保存原缓冲区地址 try { nLen = nLen < 64 ? 64: nLen; //保证最小大小 while(m_nBufferSize < nLen) { m_nBufferSize *= 2; } m_pBuffer = new char[m_nBufferSize]; //分配新缓冲区 memset(m_pBuffer, 0, sizeof(m_pBuffer)); memcpy(m_pBuffer, oBuffer, m_nOffset); //将原缓冲区中的内容拷贝到新缓冲区 delete []oBuffer; //释放原缓冲区 } catch(QException e) { return false; } return true; }

//向缓冲区中添加消息 /*

  • pBuf,要添加的数据
  • nLen,数据长度
  • 成功返回true,失败返回false */ bool DataBuffer::addMsg(char *pBuf, int nLen) { try { if(m_nOffset + nLen > m_nBufferSize) //如果缓冲过小,重新调整其大小 reBufferSize(m_nOffset + nLen); memcpy(m_pBuffer + m_nOffset, pBuf, nLen); //将新数据拷贝到缓冲区尾 m_nOffset += nLen; //修改数据偏移 } catch(QException e) { return false; } return true; }

//缓冲区复位 void DataBuffer::reset() { if(m_nOffset > 0) { memset(m_pBuffer, 0, sizeof(m_pBuffer)); m_nOffset = 0; } }

//移除缓冲区首部第一个数据包 //nLen:一个数据包的大小 void DataBuffer::poll(int nLen) { if(m_nOffset == 0 || m_pBuffer == NULL) return;

if(m_nOffset >= nLen) { memcpy(m_pBuffer, m_pBuffer + nLen, m_nOffset - nLen); m_nOffset -= nLen; } }


netcom.h



#ifndef NETTEMPLATE_H #define NETTEMPLATE_H

#include #include #include "databuffer.h"

#pragma pack(push, 1) //采用1字节对齐方式

//包头 typedef struct { int nLen; //包体长度 }PacketHead;

//封包对象:包头 + 包体 typedef struct { PacketHead head; //包头 char *body; //包体 }Packet;

#pragma pack(pop)

class NetComTemplate { public: QTcpSocket *m_tcpSocket; //通信套接字 DataBuffer m_Buffer; //套接字关联的缓冲区

void packData(char *data, int nLen);   //封包,发送
void unpackData(char *data, int nLen); //将接收到的数据放在缓冲区后,解包
virtual void recv(char *data);         //每解完一包之后的处理,留给继承的类去实现

NetComTemplate();
~NetComTemplate();

};

#endif // NETTEMPLATE_H


netcom.cpp



#include "netcom.h"

NetComTemplate::NetComTemplate() {

}

NetComTemplate::~NetComTemplate() {

}

//封包,发送 //data: 要发送的数据 //nLen: 要发送数据的长度 void NetComTemplate::packData(char *data, int nLen) { Packet p; int headLen = sizeof(PacketHead); //包头大小 p.head.nLen = nLen; //包体大小 char *buf = new char[headLen + nLen]; memcpy(buf, &p.head, headLen); //包头 memcpy(buf + headLen, data, nLen); //包体 if(m_tcpSocket != NULL) m_tcpSocket->write(buf, headLen + nLen); //发包 else qDebug() << "socket 未建立!"; }

//解包 //data: 要发送的数据 //nLen: 要发送数据的长度 void NetComTemplate::unpackData(char *data, int nLen) { m_Buffer.addMsg(data, nLen); //添加数据到缓冲区 int totalLen = m_Buffer.getDataLen(); //缓冲区中数据大小 int headLen = sizeof(PacketHead); //包头大小 while(totalLen > 0) { //不够包头,不处理 if(totalLen < headLen) { break; }

    Packet pack;                      //接收到的包
    memcpy(&pack.head, m_Buffer.m_pBuffer, headLen);   //包头
    int bodyLen = pack.head.nLen;     //包体大小
    int packLen = headLen + bodyLen;  //一包数据大小
    if(totalLen < packLen)            //不够一包数据,等够了再解析
    {
        break;
    }

    //数据足够多
    pack.body = new char[bodyLen];
    memcpy(pack.body, m_Buffer.m_pBuffer + headLen, bodyLen);  //包体
    recv(pack.body);         //处理得到的包体

    m_Buffer.poll(packLen);  //移除缓冲区中第一个数据包
    totalLen -= (packLen);
}

}

//留给继承的类去实现 //buf: 解包后得到的包体 void NetComTemplate::recv(char *data) {

}


**服务器端**


tcpserver.h



#ifndef TCPSERVER_H #define TCPSERVER_H

#include #include #include #include "netcom.h"

class myTcpServer : public QTcpServer, public NetComTemplate { Q_OBJECT

public: myTcpServer(QObject *parent, int port); ~myTcpServer();

protected: void incomingConnection(int socketDescriptor); };

#endif // TCPSERVER_H


tcpserver.cpp



#include "tcpserver.h" #include #include <stdlib.h>

myTcpServer::myTcpServer(QObject *parent, int port): QTcpServer(parent) { listen(QHostAddress::Any, port); }

myTcpServer::~myTcpServer() {

}

//出现一个新连接时调用 void myTcpServer::incomingConnection(int socketDescriptor) { m_tcpSocket = new QTcpSocket; m_tcpSocket->setSocketDescriptor(socketDescriptor);

char *d1 = "data1";
char *d2 = "data2";
char *d3 = "data3";

packData(d1, sizeof("data1"));   //封包,发送
packData(d2, sizeof("data2"));
packData(d3, sizeof("data3"));

}


**客户端**


tcpclient.h



#ifndef MYTCPCLIENT_H #define MYTCPCLIENT_H

#include #include #include "netcom.h"

class myTcpClient : public QObject, public NetComTemplate { Q_OBJECT

public: myTcpClient(QObject *parent = 0); ~myTcpClient();

void recv(char *data);           //每解完一包之后的处理

public slots: void slotRead(); };

#endif // MYTCPCLIENT_H


tcpclient.cpp



#include "tcpclient.h"

myTcpClient::myTcpClient(QObject *parent) : QObject(parent) { m_tcpSocket = new QTcpSocket; connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(slotRead())); m_tcpSocket->connectToHost(QHostAddress("127.0.0.1"), 8180); }

myTcpClient::~myTcpClient() {

}

void myTcpClient::slotRead() { while(m_tcpSocket->bytesAvailable()>0) { int n = m_tcpSocket->bytesAvailable(); //接收到的字节数 char *buf = new char[n]; m_tcpSocket->read(buf, n); //读取数据 unpackData(buf, n); //解包 delete []buf; } }

//解包之后的处理 void myTcpClient::recv(char *data) { printf("%s\n", data); }


### 完善版(使用环形缓冲区)


相对于“完整版”所改动的地方


databuffer.h



#ifndef NETDATABUFFER_H #define NETDATABUFFER_H

#define BUFFER_SIZE 1024 //初始缓冲区大小

class DataBuffer { public: char *m_pBuffer; //缓冲区 int m_nBufferSize; //缓冲区大小 int m_nStart; //数据开始位置 int m_nEnd; //数据结束位置 bool m_isFull; //缓冲区是否已满 bool m_isEmpty; //缓冲区是否为空

int getDataLen();            //获得缓冲区中数据大小
bool reBufferSize(int nLen); //调整缓冲区大小
bool addMsg(char *pBuf, int nLen);  //添加消息到缓冲区
void poll(int nLen);   //移除缓冲区首部的第一个数据包
void reset();          //缓冲区复位

public: DataBuffer(); ~DataBuffer(); };

#endif // NETDATABUFFER_H


databuffer.cpp



#include "databuffer.h" #include "string.h" #include

//构造 DataBuffer::DataBuffer() { m_nBufferSize = BUFFER_SIZE; //缓冲区大小 m_nStart = 0; //数据开始位置 m_nEnd = 0; //数据结束位置 m_isFull = false; //缓冲区是否已满 m_isEmpty = true; //缓冲区是否为空 m_pBuffer = new char[m_nBufferSize]; //分配缓冲区 memset(m_pBuffer, 0, sizeof(m_pBuffer)); //清空缓冲区 }

//析构 DataBuffer::~DataBuffer() { delete [] m_pBuffer; //释放缓冲区 m_pBuffer = NULL; m_nBufferSize = 0; }

//获得缓冲区中数据大小 int DataBuffer::getDataLen() { if(m_isFull) { return m_nBufferSize; } else if(m_nEnd < m_nStart) { return (m_nBufferSize - m_nStart) + m_nEnd; } else { return m_nEnd - m_nStart; } }

//重置缓冲区大小 bool DataBuffer::reBufferSize(int nLen) { char *oBuffer = m_pBuffer; //保存原缓冲区地址 try { nLen = nLen < 64 ? 64: nLen; //保证最小大小 while(m_nBufferSize < nLen) { m_nBufferSize *= 2; } m_pBuffer = new char[m_nBufferSize]; //分配新缓冲区 memset(m_pBuffer, 0, sizeof(m_pBuffer));

    //将原缓冲区中的内容拷贝到新缓冲区
    if(m_nStart < m_nEnd)
    {
        memcpy(m_pBuffer, oBuffer + m_nStart, m_nEnd - m_nStart);
    }
    else
    {
        int len1 = m_nBufferSize - m_nStart;
        memcpy(m_pBuffer, oBuffer + m_nStart, len1);
        memcpy(m_pBuffer + len1, oBuffer, m_nEnd);

img img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取