QTcpSocket发送数据和自定义数据

         在网络应用中,有时候我们会遇到这样的问题,用TCP不断的接收和发送不同类型的数据,数据大小,格式都不相同,起初看了qt的例子,按照例子写的程序效果相当的不好,尤其是在连续发送大数据的时候,接收端根本无法判断数据是否完整了,也不知道什么时候取读取,经过各种折腾加上看qt源码,总结出了这个方法,发送的时候,要先发送这个数据序列化后的大小,然后发送这个数据本身,接收端,首先收到了要接收数据的大小,心里有数了,等到缓存区的数据大于或者等于要接收数据大小的时候,再过去取数据,就保证了数据的正确完整和及时。最开始的时候,用QByteArry发送数据,先发送了这个QByteArry的size,然后接着发送了这个QByteArry,结果发现了一个很悲剧的事情,一万个数据里面,有几百个数据不完整,找了半天原因才发现,QByteArry在序列化过程中,首先序列化了自身的size,然后才是自身,导致序列化后大小比之前的size大了4,同样QString也是一样,就用一个自定义的结构体来做例子说明,首先自定义结构体

源码链接http://pan.baidu.com/s/1kVAAgTp

class sendStruct
{
public:
    explicit sendStruct(int Type,QString Description,QByteArray ByteData=QByteArray(0));
    int  Type;//用于区分发送的不同内容的数据,对应不同的解析方法
    QString Description;//发送内容的描述
    QByteArray ByteData;//具体发送或者接受的内容,可以将所有基本类型int,char,vector,map等或者自定义的结构体通过
                        //QDataStream序列化到ByteData中,接收端同样的方法从QDataStream中解析出来原数据
    sendStruct(){ Type=0; Description=""; ByteData=QByteArray(0);}
    int size()
        {
            int size=0;
            size=sizeof(int)+Description.size()*2+4+ByteData.size()+4;
            //序列化后QString大小为原有大小乘以2加4,QByteArry序列化后大小为原始大小加4,QString为Unicode编码每个字符占两个字节,
            //QString和QByteArry序列化过程中,首先序列化了本身大小的整形数据(qint32)到序列中,然后才是具体数据。
            return size;
        }
    int size() const
        {
            int size=0;
            size=sizeof(int)+Description.size()*2+4+ByteData.size()+4;
            return size;
        }
    sendStruct &operator=(const sendStruct &other)
        {
            Type=other.Type;
            Description=other.Description;
            ByteData=QByteArray(other.ByteData);
            return *this;
        }
#ifndef QT_NO_DATASTREAM
    friend QDataStream& operator <<(QDataStream& out,const sendStruct& senstruct)
        {
            out<<senstruct.Type
               <<senstruct.Description
               <<senstruct.ByteData;
            return out;
        }
    friend QDataStream& operator >>(QDataStream& in,sendStruct& senstruct)
        {
            in>>senstruct.Type
              >>senstruct.Description
              >>senstruct.ByteData;
            return in;
        }
#endif
};

定义TCP服务端和客户端

#ifndef TCPSERVERCONNECT_H
#define TCPSERVERCONNECT_H

#include <QObject>
#include<QTcpServer>
#include<QTcpSocket>
class sendStruct;
class TcpServerConnect : public QObject
{
    Q_OBJECT
public:
    explicit TcpServerConnect(QObject *parent = nullptr);
private:
    QTcpServer *m_server;
    QTcpSocket *m_tcpsocket;
    bool m_isGetPartData;
    int m_requestDataSize;
public slots:
    void handleSendOutData(const sendStruct&);
    void handleGetRecieveData();
    void handleNewConnection();
};

#endif // TCPSERVERCONNECT_H

#include "tcpserverconnect.h"

TcpServerConnect::TcpServerConnect(QObject *parent) : QObject(parent)
    {
        m_tcpsocket=nullptr;
        m_isGetPartData=false;
        m_requestDataSize=0;
        m_server=new QTcpServer(this);
        connect(m_server,&QTcpServer::newConnection,this,&TcpServerConnect::handleNewConnection);
        m_server->listen(QHostAddress::Any,6868);
    }

void TcpServerConnect::handleSendOutData(const sendStruct &data)
    {
        if((!m_tcpsocket)||m_tcpsocket->state()!=QAbstractSocket::ConnectedState)
            return;
        QDataStream out(m_tcpsocket);
        out<<data.size()<<data;//先发送了数据大小,在发送数据
        m_tcpsocket->flush();
        /*把需要发送的数据封装在结构体里面发送*/
    }

void TcpServerConnect::handleGetRecieveData()
    {
        if((!m_tcpsocket)||m_tcpsocket->state()!=QAbstractSocket::ConnectedState)
            return;
        if(m_isGetPartData==false){
                if(m_tcpsocket->bytesAvailable()<sizeof(int))//先要得到数据的大小
                    return;
                else
                    {
                        QDataStream in(m_tcpsocket);
                        in>>m_requestDataSize;//数据大小写入这个变量中
                        m_isGetPartData=true;//只获得了数据的大小,数据内容还未获得
                    }
            }
        if(m_isGetPartData==true){
                if(m_tcpsocket->bytesAvailable()<m_requestDataSize)//判断是否数据接收完整了,不完整就返回等待下一次判断
                    return;
                else
                    {
                        QDataStream in(m_tcpsocket);
                        sendStruct receiveData;
                        in>>receiveData;//接收到了发送端的数据
                        m_requestDataSize=0;//清空大小
                        m_isGetPartData=false;//清空标志
                        /*
                        数据接收成功,放置在receiveData中,可以做其他处理
                        doSomething(receiveData);
                        */
                        qDebug()<<"receiveData type"<<receiveData.Type;
                        qDebug()<<"receiveData Description"<<receiveData.Description;
                        qDebug()<<"receiveData ByteData"<<receiveData.ByteData;
                        if(m_tcpsocket->bytesAvailable())//如果缓存区还存在数据,继续执行
                            handleGetRecieveData();
                    }
            }
    }
void TcpServerConnect::handleNewConnection()
    {
        QTcpServer *server=static_cast<QTcpServer*>(sender());
        m_tcpsocket=server->nextPendingConnection();
        if(m_tcpsocket)
            connect(m_tcpsocket,&QTcpSocket::readyRead,this,&TcpServerConnect::handleGetRecieveData);
        
        sendStruct sendImageData;
        sendImageData.Type=0;
        sendImageData.Description=QString("this is image");
        QImage image(QSize(640,480),QImage::Format_RGB888);
        image.fill(Qt::gray);
        QBuffer buffur(sendImageData.ByteData);
        buffur.open(QIODevice::ReadWrite);
        image.save(&buffur,"JPG");
        handleSendOutData(sendImageData);
        
        sendStruct sendPointData;
        sendPointData.Type=1;
        sendPointData.Description="this is point";
        QDataStream pointStream(&sendPointData.ByteData,QIODevice::WriteOnly);
        pointStream<<QPoint(100,100);
        handleSendOutData(sendPointData);
        
    }



#ifndef TCPCLIENTCONNECT_H
#define TCPCLIENTCONNECT_H

#include <QObject>
#include<QTcpSocket>
class sendStruct;
class TcpClientConnect : public QObject
{
    Q_OBJECT
public:
    explicit TcpClientConnect(QObject *parent = nullptr);
    QTcpSocket *m_tcpsocket;
    bool m_isGetPartData;
    int m_requestDataSize;
public slots:
    void handleSendOutData(const sendStruct&);
    void handleGetRecieveData();
    void handleSocketConnected();
};

#endif // TCPCLIENTCONNECT_H


#include "tcpclientconnect.h"

TcpClientConnect::TcpClientConnect(QObject *parent) : QObject(parent)
    {
        m_tcpsocket=new QTcpSocket(this);
        m_isGetPartData=false;
        m_requestDataSize=0;
        connect(m_tcpsocket,&QTcpSocket::readyRead,this,&TcpClientConnect::handleGetRecieveData);
        connect(m_tcpsocket,&QTcpSocket::disconnected,this,&TcpClientConnect::handleSocketConnected);
        m_tcpsocket->connectToHost(QHostAddress("192.168.0.45"),6868);
        if(m_tcpsocket->waitForConnected(3000)==false){
                qDebug()<<"connect error:"<<m_tcpsocket->errorString();
            }
    }

void TcpClientConnect::handleSendOutData(const sendStruct &data)
    {
        if((!m_tcpsocket)||m_tcpsocket->state()!=QAbstractSocket::ConnectedState)
            return;
        QDataStream out(m_tcpsocket);
        out<<data.size()<<data;//先发送数据大小,在发送数据本身
        m_tcpsocket->flush();
        /*把需要发送的数据封装在结构体里面发送*/
    }

void TcpClientConnect::handleGetRecieveData()
    {
        if((!m_tcpsocket)||m_tcpsocket->state()!=QAbstractSocket::ConnectedState)
            return;
        if(m_isGetPartData==false){
                if(m_tcpsocket->bytesAvailable()<sizeof(int))//先接收数据的大小
                    return;
                else
                    {
                        QDataStream in(m_tcpsocket);
                        in>>m_requestDataSize;//数据大小写入变量
                        m_isGetPartData=true;//设置标志,只接收到了数据大小,没接收到数据全部
                    }
            }
        if(m_isGetPartData==true){
                if(m_tcpsocket->bytesAvailable()<m_requestDataSize)//判断是否接收到了完整的数据
                    return;
                else
                    {
                        QDataStream in(m_tcpsocket);
                        sendStruct receiveData;
                        in>>receiveData;//接收到了数据
                        m_requestDataSize=0;//清空大小
                        m_isGetPartData=false;//清空标志
                        /*
                        数据接收成功,放置在receiveData中,可以做其他处理
                        doSomething(receiveData);
                        */
                        qDebug()<<"receiveData type"<<receiveData.Type;
                        qDebug()<<"receiveData Description"<<receiveData.Description;
                        qDebug()<<"receiveData ByteData"<<receiveData.ByteData;
                        if(m_tcpsocket->bytesAvailable())//如果缓存区还存在数据,递归执行
                            handleGetRecieveData();
                    }
            }
    }

void TcpClientConnect::handleSocketConnected()
    {
        sendStruct sendImageData;
        sendImageData.Type=0;
        sendImageData.Description=QString("this is image");
        QImage image(QSize(640,480),QImage::Format_RGB888);
        image.fill(Qt::gray);
        QBuffer buffur(sendImageData.ByteData);
        buffur.open(QIODevice::ReadWrite);
        image.save(&buffur,"JPG");
        handleSendOutData(sendImageData);
        
        sendStruct sendPointData;
        sendPointData.Type=1;
        sendPointData.Description="this is point";
        QDataStream pointStream(&sendPointData.ByteData,QIODevice::WriteOnly);
        pointStream<<QPoint(100,100);
        handleSendOutData(sendPointData);
                
    }




  • 15
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QT是一种跨平台的C++开发框架,可以方便地实现TCP字节的发送和接收。为了实现自定义包头和内容的发送和接收,我们可以借助QT提供的QIODevice类和QDataStream类。 在发送端,首先我们需要创建一个QTcpSocket对象,并连接到服务器。然后,我们创建一个QByteArray对象,用于存储包头和内容。接下来,我们使用QDataStream对象将数据写入到QByteArray中,可以使用writeXXX方法,比如writeInt、writeString等。写入的顺序应该与接收端相对应。最后,我们将QByteArray通过QTcpSocket的write方法发送到服务器。 在接收端,首先我们需要创建一个QTcpServer对象,并监听指定的端口。当有新的连接到来时,我们使用QTcpServer的nextPendingConnection方法获得与客户端连接的QTcpSocket对象。接下来,我们通过QTcpSocket的read方法读取到来自客户端的数据,并将其写入到QByteArray中。然后,我们使用QDataStream对象从QByteArray中依次读取数据,可以使用readXXX方法来读取。读取的顺序应该与发送端相对应。最后,我们处理读取到的数据,进行进一步的操作。 在发送端和接收端,我们都需要约定好包头的格式和内容的顺序,以确保数据的正确传输和解析。可以使用固定长度的包头,比如4个字节表示包头长度,或者使用特殊字符来标识包头的开始与结束等等。根据具体的需求和情况,我们可以自定义自己的包头和内容的格式。 总之,通过QT提供的QTcpSocket、QByteArray和QDataStream等类,我们可以方便地实现TCP字节的发送和接收,并且可以自定义包头和内容的格式和顺序。这样可以帮助我们更好地满足具体的通信需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值