您的位置:首页 > 理论基础 > 计算机网络

Qt TCP之自定义通信协议

2017-07-01 17:10 399 查看
在已经实现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();

4000
//这边确实需要利用长度做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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  socket tcp qt