最全Qt---处理粘包_qt数据粘包,震撼发布

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

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

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

这里写图片描述

粘包处理后

服务器端

#pragma pack(push, 1) //按照1字节对齐
typedef struct
{
    int len;          //包头,包体长度
    char data[1024];  //包体
}NetPacket;
#pragma pack(pop)

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

    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);

    }
}

运行结果:
这里写图片描述

完整版

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

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 <QException>

//构造
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 <QTcpSocket>
#include <QDebug>
#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 <QTcpServer>
#include <QTcpSocket>
#include <QObject>
#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 <QDataStream>
#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 <QTcpSocket>
#include <QtNetwork>
#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);
}

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

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

img
img

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

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

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

et->read(buf, n); //读取数据
unpackData(buf, n); //解包
delete []buf;
}
}

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


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


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



[外链图片转存中...(img-mhsaVU8G-1715721114036)]
[外链图片转存中...(img-DLwxX97A-1715721114037)]

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

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

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值