在网络应用中,有时候我们会遇到这样的问题,用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<>(QDataStream& in,sendStruct& senstruct)
{
in>>senstruct.Type
>>senstruct.Description
>>senstruct.ByteData;
return in;
}
#endif
};
定义TCP服务端和客户端
#ifndef TCPSERVERCONNECT_H
#define TCPSERVERCONNECT_H
#include
#include
#include
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<flush();
/*把需要发送的数据封装在结构体里面发送*/
}
void TcpServerConnect::handleGetRecieveData()
{
if((!m_tcpsocket)||m_tcpsocket->state()!=QAbstractSocket::ConnectedState)
return;
if(m_isGetPartData==false){
if(m_tcpsocket->bytesAvailable()>m_requestDataSize;//数据大小写入这个变量中
m_isGetPartData=true;//只获得了数据的大小,数据内容还未获得
}
}
if(m_isGetPartData==true){
if(m_tcpsocket->bytesAvailable()>receiveData;//接收到了发送端的数据
m_requestDataSize=0;//清空大小
m_isGetPartData=false;//清空标志
/*
数据接收成功,放置在receiveData中,可以做其他处理
doSomething(receiveData);
*/
qDebug()<<"receiveData type"<bytesAvailable())//如果缓存区还存在数据,继续执行
handleGetRecieveData();
}
}
}
void TcpServerConnect::handleNewConnection()
{
QTcpServer *server=static_cast(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<
#ifndef TCPCLIENTCONNECT_H
#define TCPCLIENTCONNECT_H
#include
#include
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:"<errorString();
}
}
void TcpClientConnect::handleSendOutData(const sendStruct &data)
{
if((!m_tcpsocket)||m_tcpsocket->state()!=QAbstractSocket::ConnectedState)
return;
QDataStream out(m_tcpsocket);
out<flush();
/*把需要发送的数据封装在结构体里面发送*/
}
void TcpClientConnect::handleGetRecieveData()
{
if((!m_tcpsocket)||m_tcpsocket->state()!=QAbstractSocket::ConnectedState)
return;
if(m_isGetPartData==false){
if(m_tcpsocket->bytesAvailable()>m_requestDataSize;//数据大小写入变量
m_isGetPartData=true;//设置标志,只接收到了数据大小,没接收到数据全部
}
}
if(m_isGetPartData==true){
if(m_tcpsocket->bytesAvailable()>receiveData;//接收到了数据
m_requestDataSize=0;//清空大小
m_isGetPartData=false;//清空标志
/*
数据接收成功,放置在receiveData中,可以做其他处理
doSomething(receiveData);
*/
qDebug()<<"receiveData type"<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<