传输文件时,简单的文本文件可以以字符串的形式传输。一般的无格式文件则以二进制格式传输。
编写程序涉及的buffer类有
QFile QByteArray QString QTCPSocket 如果用其他套接字的话还会用到 char[]数组。当传输文件时不可避免取得数据操作,存储数据类型转换,写入数据操作。
在文本方式中:
首先需要考虑字符编码,有中文字符时,得转为utf-8在通过套接字发送,另一端接收到后在转回来(QString的toutf8()和fromutf8()操作)。
考虑发送方式,是一行一行的读取和发送还是其他方式(简单的文本文件一般一行一行的传输,但是文件稍大就传输慢了)。
发送:
voidS5BSendClient::sendTextModeFile(QFile&file)
{
QTextStreamfilestream(&file);
QStringline=filestream.readLine();
qDebug()<<line;
while(!line.isNull()){
sendFileData(line);
line=filestream.readLine();
}
if(line.isNull())
{
if(file.isOpen())
{
file.close();
}
sendFileDone();
}
}
voidS5BSendClient::sendFileData(QStringline)
{
对应于文本模式的发送数据
sendTextLineString(line);
}
voidS5BSendClient::sendTextLineString(QStringline)
{
QByteArrayutf8line=line.toUtf8();
发送4个字节的字符串长度数值
//quint32len=(line.length()*GBKCHARSIZE)+1;
quint32len=utf8line.length()+1;
charlenbuf[4]={0};
lenbuf[0]=len&0xff;
lenbuf[1]=(len>>8)&0xff;
lenbuf[2]=(len>>16)&0xff;
lenbuf[3]=(len>>24)&0xff;
send(lenbuf,4);
发送字符串
charlinebuf[4*1024]={0};
strcpy(linebuf,utf8line);
send(linebuf,len);
sendedSize+=len;
qDebug()<<tr("havesend%1/%2filedata!").arg(sendedSize).arg(m_filesize);
}
接收:
voidS5BRecvClient::recvTextFileData()
{
charbuffer[4*1024]={0};
recv(buffer,recvLen);
inthasRecv=recvLen;
QByteArrayrecvdata=QByteArray::fromRawData(buffer,recvLen);
QStringrecvstr=QString::fromUtf8(recvdata);
qDebug()<<recvdata;
state=negFileDatalen;
checkRecvState();
//把接收的数据放到文件中
QTextStreamout(m_pRecvfile);
out<<recvstr<<endl;
recvSize+=hasRecv;
qDebug()<<tr("haverecv%1/%2data!").arg(recvSize).arg(m_filesize);
//读完文件行数据后,在触发一次读文件行操作,用来读文件行长度
QTimer::singleShot(0,this,SLOT(slot_readyRead()));
}
在二进制方式中:
不需要考虑编码,但是无法一行一行的传输,可以以块作单位传输。
发送:
voidS5BSendClient::sendDataModeFile(QFile&file)
{
while(!file.atEnd())
{
QByteArraymax4M=file.read(4*1024);
sendMax4MData(max4M);
}
if(file.isOpen())
{
file.close();
}
sendFileDone();
}
voidS5BSendClient::sendMax4MData(QByteArraymax4M)
{
QByteArrayblock;
QDataStreamfilestream(&block,QIODevice::WriteOnly);
filestream.setVersion(QDataStream::Qt_4_0);
filestream<<(quint32)0;
filestream<<max4M;
filestream.device()->seek(0);
filestream<<(quint32)(block.size()-sizeof(quint32));
getSocket()->write(block);
}
接收:
voidS5BRecvClient::recvDataFileData()
{
charbuffer[4*1024]={0};
recv(buffer,recvLen);
inthasRecv=recvLen;
//把接收的数据放到文件中
m_pRecvfile->write(buffer,hasRecv);
qDebug()<<m_pRecvfile->flush();
recvSize+=hasRecv;
qDebug()<<tr("haverecv%1/%2data!").arg(recvSize).arg(m_filesize);
//修改状态(不需在读)
state=negFileDatalen;
checkRecvState();
//尝试在读一次,或导致超时
QTimer::singleShot(0,this,SLOT(slot_readyRead()));
}
两种方式传输在Qt的套接字编程时都得考虑发送和接收数据大小的协商。
也就是说发送数据前添上数据的长度,后跟该数据长度大小的数据。
接收时,先接收特定字节大小的长度值,在接收该值所表示的数据字节数。
voidS5BRecvClient::recvFileDataLen()
{
if(!issendfile)
{
quint32recvdatalen;
charbuffer[4]={0};
recv(buffer,recvLen);
if(!isLittleEndian())
{
recvdatalen=charToUInt8(buffer[0])+
charToUInt8(buffer[1])*256+
charToUInt8(buffer[2])*256*256+
charToUInt8(buffer[3])*256*256*256;
}else
{
recvdatalen=charToUInt8(buffer[3])+
charToUInt8(buffer[2])*256+
charToUInt8(buffer[1])*256*256+
charToUInt8(buffer[0])*256*256*256;
}
qDebug()<<recvdatalen;
state=negFileData;
recvLen=recvdatalen;
//读完文件行长度后,在触发一次读数据操作,用来都文件行数据
QTimer::singleShot(0,this,SLOT(slot_readyRead()));
}
}
boolS5BRecvClient::isLittleEndian()
{
unionuni{
inti;
charc;
}test;
test.i=1;
return(test.c==1);
}
quint8S5BRecvClient::charToUInt8(chara)
{
quint8r=a-'\0';
qDebug()<<r;
returnr;
}
voidS5BRecvClient::recvFileData()
{
if(!issendfile)
{
if(m_mode==TextMode)
{
recvTextFileData();
}elseif(m_mode==DataMode)
{
recvDataFileData();
}
}
}