概述:QTcpSocket和QTcpServer可以实现TCP客户端和服务器端通信,本文介绍基于块的通信方式
基于块的传输协议把数据作为二进制块进行传输。每一个块都由一个大小字段及其包含的数据域组成
程序界面
一个数服务端socket一个是客户端socket,客户端socket可以从服务器端socket下载文件
运行过程
客户端精度条显示文件下载进度。
test.rar表示服务器要发送的文件,testreceived.rar表示客户端收到的文件
因为测试服务器和客户端在同一目录下,所以该程序把收到的test.rar命名为testreceived.rar,以做区分
核心代码
connect(&tcpSocketClient,&QTcpSocket::connected,this,&FileTransferSocket::slotConnected);
connect(&tcpSocketClient,&QTcpSocket::disconnected,this,&FileTransferSocket::slotDisconnected);
connect(&tcpSocketClient,&QTcpSocket::readyRead,this,&FileTransferSocket::slotReadyRead);
connect(&tcpSocketClient,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(slotError(QAbstractSocket::SocketError)));
slotReadyRead当对方socket有数据过来将会调用此槽函数
1、tcpSocketClient.connectToHost("127.0.0.1",8888);
连接到服务器端socket
2、发送数据到连接的socket
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_1);
out<<quint16(0)<<QString("downrequest");
out.device()->seek(0);
out << quint16(block.size() - sizeof(quint16));
tcpSocketClient.write(block);
每次发送数据都是以块为单位的,上面代码发送一个块的数据
但是需要注意的地方是,数据在网络传输中并不是以块为单位,也就是发送一整块数据,接收的socket可能不是一次正好接收一整个块
可能收到一个完整的块或者一个块的一部分、一个块和另一个块的一部分、若干个块或者亦可能是所有的块,在接收端socket需要判断。
先看看块的结构形式
一个块包含两部分,一部分表示数据区域的大小,一部分表示数据区域
out<<quint16(0)<<QString("downrequest");
这段代码的quint16(0)用来存放数据区域的大小,QString("downrequest")表示数据区域
out.device()->seek(0);
out << quint16(block.size() - sizeof(quint16));
这两行代码,分别是移到首位置,即数据数据区域大小
然后给数据区域填充数据的实际大小值.
3、我们来看看接收端socket如何接收数据
在这里定义一个nextBlockSize变量
用来存放每个块的大小。
bytesAvailable()表示网络里面传过来的可用字节数,如果可用字节数已经大于或者等于sizeof(quint16)
还记得quint16表示什么吗?这个区域存放的就是块的数据区域大小。
如果可用字节数已经大于或者等于sizeof(quint16),这样我们就可以取出这个块的数据区域大小
in >> nextBlockSize;
然后再判断bytesAvailable()是否大于等于nextBlockSize如果不是说明这个块的数据还未完全传过来,等待网络数据传输(千万不要取已接收的数据,因为是不完整的)
如果大于等于nextBlockSize,说明这一块的数据区域已经完全传过来了。
//数据块完整可以接收
QString clientrequest;
in >> clientrequest;
然后把数据区域的数据放到clientrequest里面。
到此通过块的形式发送数据就完成了
4、下面开始文件的传输
先获取本地文件,打文件大小文件名称等信息传到客户端socket,当然上面代码是写死的文件名,你可以通过file直接获取文件名。
上面的代码还未正式发送文件,只是把文件的一些信息发送给客户端socket。
5、
connect(this, SIGNAL(bytesWritten(qint64)), this, SLOT(slotBytesWritten(qint64)));
bytesWritten(qint64))这个信号每次数据写入设备后会激发
在此槽函数里面读取文件并发送文件到客户端
文件分多次发送,每当有数据写入的设备改槽函数都执行,直到文件发送完毕
6.我们再看看文件接收端
这里就不做详细讲解了,主要就是从sokcet读取说有数据,然后文件写入