qt4 tcp QTcpSocket QTcpServer 傳輸文件

tcpSender (客戶端無QTcpServer)
  1. private:  
  2.         QTcpSocket *tcpClient;  
  3.         QFile *localFile;  //要發送的文件  
  4.         qint64 totalBytes;  //數據總大小  
  5.         qint64 bytesWritten;  //已經發送數據大小  
  6.         qint64 bytesToWrite;   //剩餘數據大小  
  7.         qint64 loadSize;   //每次發送數據的大小  
  8.         QString fileName;  //保存文件路徑  
  9.         QByteArray outBlock;  //數據緩衝區,即存放每次要發送的數據  
  10. private slots:  
  11.     void send();  //連接服務器  
  12.     void startTransfer();  //發送文件大小等信息  
  13.     void updateClientProgress(qint64); //發送數據,更新進度條  
  14.     void displayError(QAbstractSocket::SocketError); //顯示錯誤  
  15.     void openFile();  //打開文件  
  1. Widget::Widget(QWidget *parent) :  
  2.     QWidget(parent),  
  3.     ui(new Ui::Widget)  
  4. {  
  5.     ui->setupUi(this);  
  6.     loadSize = 4*1024;  
  7.     totalBytes = 0;  
  8.     bytesWritten = 0;  
  9.     bytesToWrite = 0;  
  10.     tcpClient = new QTcpSocket(this);  
  11.     connect(tcpClient,SIGNAL(connected()),this,SLOT(startTransfer()));  
  12.     //當連接服務器成功時,發出connected()信號,我們開始傳送文件  
  13.     connect(tcpClient,SIGNAL(bytesWritten(qint64)),this,SLOT(updateClientProgress(qint64)));  
  14.     //當有數據發送成功時,我們更新進度條  
  15.     connect(tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));  
  16.     ui->sendButton->setEnabled(false);  
  17.     ui->hostLineEdit->setText("192.168.1.100");  
  18.     ui->portLineEdit->setText("6666");  
  19. }  
  1. void Widget::openFile()   //打開文件  
  2. {  
  3.     fileName = QFileDialog::getOpenFileName(this);  
  4.     if(!fileName.isEmpty())  
  5.     {  
  6.         ui->sendButton->setEnabled(true);  
  7.         ui->clientStatusLabel->setText(tr("打開文件 %1 成功!").arg(fileName));  
  8.     }  
  9. }  
  10.   
  11.   
  12. void Widget::send()   //連接到服務器,執行發送  
  13. {  
  14.     ui->sendButton->setEnabled(false);  
  15.     bytesWritten = 0;  
  16.     //初始化已發送字節爲0  
  17.     ui->clientStatusLabel->setText(tr("連接中…"));  
  18.     tcpClient->connectToHost(QHostAddress(ui->hostLineEdit->text()),ui->portLineEdit->text().toInt());//連接  
  19. }  
  20.   
  21. void Widget::on_openButton_clicked() //打開按鈕  
  22. {  
  23.     openFile();  
  24. }  
  25.   
  26. void Widget::on_sendButton_clicked() //發送按鈕  
  27. {  
  28.     send();  
  29. }  

  1. void Widget::startTransfer()  //實現文件大小等信息的發送  
  2. {  
  3.     localFile = new QFile(fileName);  
  4.     if(!localFile->open(QFile::ReadOnly))  
  5.     {  
  6.         qDebug() << "open file error!";  
  7.         return;  
  8.     }  
  9.     totalBytes = localFile->size();  
  10.     //文件總大小  
  11.     QDataStream sendOut(&outBlock,QIODevice::WriteOnly);  
  12.     sendOut.setVersion(QDataStream::Qt_4_6);  
  13.     QString currentFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);  
  14.     sendOut << qint64(0) << qint64(0) << currentFileName;  
  15.     //依次寫入總大小信息空間,文件名大小信息空間,文件名---僅是文件名不含路徑,沒必要含路徑  
  16.     totalBytes += outBlock.size();  
  17.     //這裏的總大小是文件名大小等信息和實際文件大小的總和  
  18.     sendOut.device()->seek(0);  
  19.     sendOut<<totalBytes<<qint64((outBlock.size() - sizeof(qint64)*2));  
  20.     //totalBytes是文件總大小,即兩個quint64的大小+文件名+文件實際內容的大小  
  21.     //qint64((outBlock.size() - sizeof(qint64)*2))得到的是文件名大小  
  22.     bytesToWrite = totalBytes - tcpClient->write(outBlock);  
  23.     //發送完頭數據後剩餘數據的大小,即文件實際內容的大小  
  24.     ui->clientStatusLabel->setText(tr("已連接"));  
  25.     outBlock.resize(0);  
  26. }  

  1. void Widget::updateClientProgress(qint64 numBytes) //更新進度條,實現文件的傳送  
  2. {  
  3.     bytesWritten += (int)numBytes;  
  4.     //已經發送數據的大小  
  5.     if(bytesToWrite > 0) //如果已經發送了數據  
  6.     {  
  7.         outBlock = localFile->read(qMin(bytesToWrite,loadSize));  
  8.       //每次發送loadSize大小的數據,這裏設置爲4KB,如果剩餘的數據不足4KB,  
  9.       //就發送剩餘數據的大小  
  10.         bytesToWrite -= (int)tcpClient->write(outBlock);  
  11.        //發送完一次數據後還剩餘數據的大小  
  12.         outBlock.resize(0);  
  13.        //清空發送緩衝區  
  14.     }  
  15.     else  
  16.     {  
  17.         localFile->close(); //如果沒有發送任何數據,則關閉文件  
  18.     }  
  19.     ui->clientProgressBar->setMaximum(totalBytes);  
  20.     ui->clientProgressBar->setValue(bytesWritten);  
  21.     //更新進度條  
  22.     if(bytesWritten == totalBytes) //發送完畢  
  23.     {  
  24.         ui->clientStatusLabel->setText(tr("傳送文件 %1 成功").arg(fileName));  
  25.         localFile->close();  
  26.         tcpClient->close();  
  27.     }  
  28. }  

  1. void Widget::displayError(QAbstractSocket::SocketError) //顯示錯誤  
  2. {  
  3.     qDebug() << tcpClient->errorString();  
  4.     tcpClient->close();  
  5.     ui->clientProgressBar->reset();  
  6.     ui->clientStatusLabel->setText(tr("客戶端就緒"));  
  7.     ui->sendButton->setEnabled(true);  
  8. }  

流程:
1創建tcpSocket ,tcpClient= new QTcpSocket(this);
2.關聯信號connected和槽函數startTransfer,connect(tcpClient,SIGNAL(connected()),this,SLOT(startTransfer()));其中信號connected在連接服務器成功(即本客戶端執行 tcpClient->connectToHost,得到服務器的成功響應)時發射,此時執行startTransfer開始傳輸文件頭數據
3.關聯信號bytesWritten和槽函數updateClientProgress,connect(tcpClient,SIGNAL(bytesWritten(qint64)),this,SLOT(updateClientProgress(qint64)));其中信號bytesWritten在當有數據發送成功時(即本客戶端執行 tcpClient->write(outBlock);服務器的成功收outBlock時)時發射,此時執行updateClientProgress來更新進度條並接着發送剩餘數據
4.連接到某個ip的某個端口,tcpClient->connectToHost(QHostAddress(ui->hostLineEdit->text()),ui->portLineEdit->text().toInt())
5.實現槽函數startTransfer,開始傳輸文件頭數據
5.實現槽函數updateClientProgress,更新進度條,並接着發送剩餘數據
其中發送的文件頭包括一個quint64(記錄文件總大小,包括sizeof(quint64)+sizeof(quint64)+文件名+文件實際內容),一個quint64(記錄文件名大小),一個文件名不含路徑,一個文件實際內容


tcpReceiver
  1. private:  
  2.         QTcpServer tcpServer;  
  3.         QTcpSocket *tcpServerConnection;  
  4.         qint64 totalBytes;  //存放總大小信息  
  5.         qint64 bytesReceived;  //已收到數據的大小  
  6.         qint64 fileNameSize;  //文件名的大小信息  
  7.         QString fileName;   //存放文件名  
  8.         QFile *localFile;   //本地文件  
  9.         QByteArray inBlock;   //數據緩衝區  
  10. private slots:  
  11.     void start();   //開始監聽  
  12.     void acceptConnection();  //建立連接  
  13.     void updateServerProgress();  //更新進度條,接收數據  
  14.     void displayError(QAbstractSocket::SocketError socketError);  

  1. Widget::Widget(QWidget *parent) :  
  2.     QWidget(parent),  
  3.     ui(new Ui::Widget)  
  4. {  
  5.     ui->setupUi(this);  
  6.         totalBytes = 0;  
  7.         bytesReceived = 0;  
  8.         fileNameSize = 0;  
  9.         connect(&tcpServer,SIGNAL(newConnection()),this,SLOT(acceptConnection()));  
  10.     //當發現新連接時發出newConnection()信號  
  11. }  
  1. void Widget::start() //開始監聽  
  2. {  
  3.     ui->startButton->setEnabled(false);  
  4.     bytesReceived =0;  
  5.     if(!tcpServer.listen(QHostAddress("192.168.1.100"),6666))  
  6.     {  
  7.         qDebug() << tcpServer.errorString();  
  8.         close();  
  9.         return;  
  10.     }  
  11.     ui->serverStatusLabel->setText(tr("監聽"));  
  12. }  
  13.   
  14. void Widget::on_startButton_clicked() //開始監聽按鈕  
  15. {  
  16.     start();  
  17. }  

  1. void Widget::acceptConnection()  //接受連接  
  2. {  
  3.     tcpServerConnection = tcpServer.nextPendingConnection();  
  4.     connect(tcpServerConnection,SIGNAL(readyRead()),this,SLOT(updateServerProgress()));  
  5.     connect(tcpServerConnection,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));  
  6.     ui->serverStatusLabel->setText(tr("接受連接"));  
  7.     tcpServer.close();  
  8. }  

  1. void Widget::updateServerProgress()  //更新進度條,接收數據  
  2. {  
  3.     QDataStream in(tcpServerConnection);  
  4.     in.setVersion(QDataStream::Qt_4_6);  
  5.   
  6.     if(bytesReceived <= sizeof(qint64)*2)  
  7.     { //如果接收到的數據小於16個字節,那麼是剛開始接收數據,我們保存到//來的頭文件信息  
  8.   
  9.         if((tcpServerConnection->bytesAvailable() >= sizeof(qint64)*2)&& (fileNameSize == 0))  
  10.         { //接收數據總大小信息和文件名大小信息  
  11.             in >> totalBytes >> fileNameSize;  
  12.             bytesReceived += sizeof(qint64) * 2;  
  13.         }  
  14.   
  15.         if((tcpServerConnection->bytesAvailable() >= fileNameSize)  
  16.                 && (fileNameSize != 0))  
  17.         {  //接收文件名,並建立文件  
  18.             in >> fileName;  
  19.             ui->serverStatusLabel->setText(tr("接收文件 %1 …").arg(fileName));  
  20.             bytesReceived += fileNameSize;  
  21.             localFile = new QFile(fileName);  
  22.             if(!localFile->open(QFile::WriteOnly))  
  23.             {  
  24.                 qDebug() << "open file error!";  
  25.                 return;  
  26.             }  
  27.         }  
  28.   
  29.         else return;  
  30.     }  
  31.   
  32.   
  33.     if(bytesReceived < totalBytes)  
  34.     {  //如果接收的數據小於總數據,那麼寫入文件  
  35.         bytesReceived += tcpServerConnection->bytesAvailable();  
  36.         inBlock = tcpServerConnection->readAll();  
  37.         localFile->write(inBlock);  
  38.         inBlock.resize(0);  
  39.     }  
  40.   
  41.     ui->serverProgressBar->setMaximum(totalBytes);  
  42.     ui->serverProgressBar->setValue(bytesReceived);  
  43.     //更新進度條  
  44.     if(bytesReceived == totalBytes)  
  45.   
  46.     { //接收數據完成時  
  47.         tcpServerConnection->close();  
  48.         localFile->close();  
  49.         ui->startButton->setEnabled(true);  
  50.         ui->serverStatusLabel->setText(tr("接收文件 %1 成功!").arg(fileName));  
  51.     }  
  52. }  

  1. void Widget::displayError(QAbstractSocket::SocketError) //錯誤處理  
  2. {  
  3.     qDebug() << tcpServerConnection->errorString();  
  4.     tcpServerConnection->close();  
  5.     ui->serverProgressBar->reset();  
  6.     ui->serverStatusLabel->setText(tr("服務端就緒"));  
  7.     ui->startButton->setEnabled(true);  
  8. }  

流程
1.創建 QTcpServer tcpServer;使之監聽本機的某個端口,tcpServer.listen(QHostAddress("192.168.1.100"),6666)
2,關聯信號newConnection和槽函數sendMessage, connect(&tcpServer,SIGNAL(newConnection()),this,SLOT(acceptConnection()));其中信號newConnection在有客戶端的連接請求(即客戶端執行tcpSocket->connectToHost)時發射,此時執行acceptConnection
3.實現槽函數acceptConnection,在裏面
從tcpServer取得已經建立但掛起的QTcpSocket連接
tcpServerConnection = tcpServer.nextPendingConnection();
並關聯信號readyRead和槽函數updateServerProgress,
connect(tcpServerConnection,SIGNAL(readyRead()),this,SLOT(updateServerProgress()));其中信號readyRead在有新的數據到達時發射,此時執行updateServerProgress函數把接收到的數據保存到文件,並更新進度條
4.實現槽函數updateServerProgress

從上篇和此篇可看出基於客戶和服務器的tcp編程:
需要在服務器端有QTcpServer監視某個端口,在客戶端使用QTcpSocket發起連接,然後在服務器端獲取連接QTcpSocket,然後雙方就使用QTcpSocket提供的方法來互發數據其中,
發送方使用 QByteArray block; clientConnection->write(block);發送數據,使用bytesWritten信號得知數據已經發送完成
接收方使用readyRead信號得知有數據到達,使用  QDataStream in(tcpSocket); in >> message;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章