Qt-socket网络 数据粘包处理

Qt-socket网络 数据粘包处理

UDP

例子1

.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>
#include <QHostAddress>
#include <QHostInfo>

#define BUFFER_SIZE 1024 // 数据包缓冲区大小
#define RECV_BUFFER_SIZE 2048 // 接收缓冲区大小

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    QUdpSocket *m_udpSocket;
    QByteArray m_recvBuffer; // 接收缓冲区
    QByteArray m_dataBuffer; // 数据缓冲区
    QHostAddress m_senderIp; // 发送方IP地址
    quint16 m_senderPort; // 发送方端口

signals:
    void dataReceived(QByteArray data);

private slots:
    void processPendingDatagrams();
};
#endif // WIDGET_H

.cpp

#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_udpSocket=new QUdpSocket(this);

    m_udpSocket->bind(QHostAddress("127.0.0.1"),8080);

    connect(m_udpSocket,&QUdpSocket::readyRead,this,&Widget::processPendingDatagrams);
}

Widget::~Widget()
{
}

void Widget::processPendingDatagrams()
{
    while (m_udpSocket->hasPendingDatagrams())
    {
        // 从套接字中读取数据报
        m_recvBuffer.resize(m_udpSocket->pendingDatagramSize());

        m_udpSocket->readDatagram(m_recvBuffer.data(),m_recvBuffer.size(),&m_senderIp,&m_senderPort);

        // 将数据报添加到数据包缓冲区中
        m_dataBuffer.append(m_recvBuffer);

        // 处理数据包
        while (m_dataBuffer.size()>=BUFFER_SIZE)
        {
            // 从数据包缓冲区中读取一个完整的数据报
            QByteArray data=m_dataBuffer.left(BUFFER_SIZE);

            // 发送信号,进一步的处理...
            emit dataReceived(data);

            // 从数据包缓冲区中移除已处理的数据包
            m_dataBuffer.remove(0,BUFFER_SIZE);
        }
    }
}

例子2

/*
    协议头:2字节
    数据长度:2字节 不包括协议头和数据长度
    数据体
*/
void sendUdpPacket(QString ip,int port,QByteArray data); // 发送udp数据包
void receiveUdpPacket(); // 接收udp数据包

void Widget::sendUdpPacket(QString ip,int port,QByteArray data)
{
    QUdpSocket socket;
    QByteArray packet;

    packet.append((char)0x12); // 协议头
    packet.append((char)0x00); // 协议头
    packet.append((char)((data.size()>>8) & 0xFF)); // 数据长度高字节
    packet.append((char)(data.size() & 0xFF)); // 数据长度低低字节
    packet.append(data);
    socket.writeDatagram(packet,QHostAddress(ip),port);
}

void Widget::receiveUdpPacket()
{
    /*
    在接收数据的时候,我们使用了一个静态变量 cache 来缓存未处理完整的数据。
    在每次接收到数据后,将数据追加到 cache 中,并检查 cache 中是否包含完整的数据包。
    如果包含完整数据包,就使用 mid() 函数将数据截取出来,并将 cache 中对应的数据移除。
    由于可能存在多个数据包粘在一起的情况,因此在检查完一个数据包后,
    需要继续检查 cache 中是否还有剩余的数据。如果有,则继续处理。

    */

    QUdpSocket socket;
    while (socket.hasPendingDatagrams())
    {
        QByteArray dataGram;
        dataGram.resize(socket.pendingDatagramSize());
        QHostAddress senderIP;
        quint16 senderPort;
        socket.readDatagram(dataGram.data(),dataGram.size(),&senderIP,&senderPort);

        // 处理粘包
        static QByteArray cache;
        cache.append(dataGram);

        while (cache.size()>=4)
        {
            // 检查协议头是否合法
            if ((quint8)cache.at(0)==0x12 && (quint8)cache.at(1)==0x00)
            {
                // 获得数据长度
                // 左移变高字节+低字节
                quint16 dataSize=((quint8)cache.at(2)<<8)+((quint8)cache.at(3));

                if (cache.size()>=4+dataSize)
                {
                    QByteArray data=cache.mid(4,dataSize);
                    cache=cache.mid(4+dataSize);

                    // 处理数据 code...
//                    handleData(data);
                }
                else
                {
                    break;
                }
            }
            else
            {
                // 丢弃无效数据
                cache.remove(0,1);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值