QTcpSocket发送数据和自定义数据

来源:互联网 发布:军用望远镜软件下载 编辑:程序博客网 时间:2024/05/22 06:57

         在网络应用中,有时候我们会遇到这样的问题,用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_OBJECTpublic:    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_OBJECTpublic:    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);                    }