概述: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讀取說有數據,然后文件寫入
