Qt TCP之自定义通信协议

来源:互联网 发布:在哪里买正版windows 编辑:程序博客网 时间:2024/05/22 01:34

在已经实现socket通信的前提下,设计了如下的通信格式:

这里写图片描述

假设cmd定义如下:

#ifndef CMD_H#define CMD_H//服务器------->客户端#define     Connet_Success         0x0F00   //连接成功应答包#define     Login_answer           0x0F01   //登陆结果(也是QString 的一种)#define     QString_send           0x0F02   //发送字符串#define     QFile_send             0x0F03   //发送文件#define     Struct_send            0x0F03   //发送结构体//客户端------->服务器#define     Login                  0x0D00   //客户端登陆#endif // CMD_H

一些变量说明:

    qint64 totalBytes;      //一个数据包MSG部分的完整大小    qint64 recvdBytes;      //已经收到的字节数    qint64 serverCmd;       //接受数据包的类型    QByteArray inBlock;     //接受缓冲    QByteArray m_buffer;    //缓存上一次或多次的未处理的数据    QFile *localFile;    QSqlDatabase db;    QMutex mutex;
  • 发送QString字符串
void mySocket::sendMSG(QString msg, qint64 cmd){    if(!isValid()) //确保连接仍然有效    {        qDebug()<<"losing connect.......";        return;    }    /********************构造数据包************************/    qint64 totalBytes = 0;    QByteArray block; //用于暂存我们要发送的数据    QDataStream output(&block,QIODevice::WriteOnly);//使用数据流写入数据    output.setVersion(QDataStream::Qt_5_2);    totalBytes = msg.toUtf8().size();    //  totalBytes+cmd  构成了包头 长度为2*qint64 也就是头文件中定义的MINSIZE    output<<qint64(totalBytes)<<qint64(cmd); //将命令内容的长度、命令类型写入到数据流中去 cmdw为QString_send类型    totalBytes += block.size();//加上上一行内容的长度    output.device()->seek(0);//回到数据流的开始位置    output<<totalBytes; //所有内容(msg+cmd+qint64)的长度    write(block);    block.resize(0);//清空    for(int i=0;i<10000;i++); //延时    block = msg.toUtf8();    write(block);//发送命令内容    block.resize(0);}
  • 发送QFile文件
void mySocket::sendQFile(QString path){   QFile localFile(path);   if(!localFile.open(QFile::ReadOnly))       return;   qDebug()<<"open data file success";   qint64 totalBytes = 0;   QByteArray outBlock;   outBlock.resize(0);   QDataStream sendOut(&outBlock,QIODevice::WriteOnly);   sendOut.setVersion(QDataStream::Qt_5_2);   totalBytes = localFile.size();   sendOut<<qint64(totalBytes)<<qint64(QFile_send);   totalBytes += outBlock.size();   sendOut.device()->seek(0);   sendOut<<totalBytes;   write(outBlock);   outBlock.resize(0);   outBlock = localFile.readAll();   write(outBlock);   outBlock.resize(0);   localFile.close();}
  • 发送Struct结构体文件
void mySocket::sendStructData(){    if(!isValid())        return;    //构造数据包    qint64 totalBytes = 2*sizeof(qint64) + sizeof(stu_stateData);//stu_stateData是    QByteArray outBlock;    QDataStream sendOut(&outBlock,QIODevice::WriteOnly);    sendOut.setVersion(QDataStream::Qt_5_2);    outBlock.resize(totalBytes);    //向缓冲区写入文件头    sendOut<<totalBytes<<qint64(Struct_send);      //向缓冲区写入文件数据    mutex.lock();    memcpy(outBlock.data() + 2*sizeof(qint64),&stateData,sizeof(stu_stateData));    mutex.unlock();    write(outBlock);    outBlock.resize(0);}
  • 接收QString、QFile和Struct结构体文件
void mySocket::readMsg(){//如果不存在数据,就直接结束    if(bytesAvailable() <= 0)    {        return;    }    //从缓存区中去除数据,但是不确定取出来的字节数    QByteArray  buffer;    buffer = readAll();    m_buffer.append(buffer);    unsigned int totalLen = m_buffer.size();    //这边确实需要利用长度做while循环,因为有可能一下子读取到两条以上的完整记录,就需要进行循环处理了;    //超过一条完整小于第二条完整记录时,如果已经达到包头长度就先把包头保存下来,整个过程循环往复    while(totalLen)    {        //与QDataStream绑定,方便操作        QDataStream  packet(m_buffer);        packet.setVersion(QDataStream::Qt_5_2);        //不够包头长度的不处理,结束while循环        if(totalLen < MINSIZE)            break;        //将包头读入了进来按照定义的协议 先读命令长度,再读命令的类型        packet>>totalBytes>>serverCmd;         //缓存中的内容长度没有达到命令的长度,那就先结束,等足够了再来解析        if(totalLen<totalBytes)            break;        //足够长了就开始解析        QDir dir(sysFilePath);      //系统文件目录        if(!dir.exists())            dir.mkdir(sysFilePath);        switch(serverCmd)        {        case QString_send :    //接收QString        {            qDebug()<<"开始接收字符串...";            QByteArray datas = m_buffer.mid(MINSIZE,totalBytes-MINSIZE);            QString strInfo;//数据包中的message            strInfo.prepend(datas);            qDebug()<<strInfo;//输出接收到的QString            break;        }        case QFile_send :    //接收文件         {            qDebug()<<"收到文件";            qDebug()<<"文件大小为:"<<totalBytes;            tmpfileName = sysFilePath+"file/something.txt"; //也可以是其他文件类型 如.db            localFile = new QFile(tmpfileName);//真正的文件路径            if(!localFile->open(QIODevice::WriteOnly)) //写的方式打开该文件            {                qDebug()<<tr("无法打开文件%1:\n%2.").arg(tmpfileName).arg(localFile->errorString());                return;            }            localFile->resize(0);//清空文件                     QByteArray datas = m_buffer.mid(MINSIZE,totalBytes-MINSIZE);            localFile->write(datas);            localFile->close();            break;          }        case  Struct_send:         {            QByteArray realStateData = inBlock.mid(MINSIZE,totalBytes-MINSIZE);            motionMutex.lock();            memcpy(&motionData,realStateData.data(),sizeof(stu_stateData));   //motionData是一个stu_stateData结构体变量            motionMutex.unlock();            break;         }        }        //缓存多余的数据     buffer = m_buffer.right(totalLen - totalBytes); //截取下一个数据包的数据,留作下次读取    totalLen = buffer.size();    //更新多余的数据    m_buffer = buffer;    }}
原创粉丝点击