分享

在QT中使用TCP协议进行文件传输(可以单向循环传输)

 zkc_younger 2017-03-22

大致步骤如下:

1、服务器端设置监听套接字,开始监听;

2、客户端在连接成功时开始传送文件,有connected()信号连接send()槽,send()发送文件头信息,包括文件名、文件总大小和文件名大小等;

3、传送完文件头信息时开始传送文件内容,有bytesWritten(qint64)信号连接到goOnSend(qint64)槽,前者是当想套接字写入数据时会出发的信号,即当已经想套接字写入数据,就继续传送数据,有send()传送文件头信息开始触发,直到文件传完为止。

4、在服务器端,首先接收文件头信息,然后读取文件名来用新建文件的方式打开一个文件,然后读取文件名即文件等大小信息,用来更新进度条和继续接收数据;

5、实现循环传输,在客户端,因为第一次send()是由connected()信号触发的,之后的每次传送应该手动调用send();在服务器端,在有新数据到达时,会判断是否为头文件,因此在每次文件传完的时候将byteReceived重置为0,即下一次再接收到数据的时候依据byteReceived判断的结果就是一个新文件了。

上代码吧,有些对象名和函数名取得不好,注释应该可以让大家理解了。


客户端代码:

widget.h

  1. #ifndef WIDGET_H  
  2. #define WIDGET_H  
  3.   
  4. #include <QWidget>  
  5. #include <QTcpSocket>  
  6. #include <QFile>  
  7. #include <string>  
  8.   
  9. namespace Ui {  
  10. class Widget;  
  11. }  
  12.   
  13. class Widget : public QWidget  
  14. {  
  15.     Q_OBJECT  
  16.   
  17. public:  
  18.     explicit Widget(QWidget *parent = 0);  
  19.     ~Widget();  
  20.       
  21. private:  
  22.     Ui::Widget *ui;  
  23.   
  24.     QTcpSocket *tcpClient;  
  25.     QFile *localFile;  
  26.     QString fileName;  //文件名  
  27.   
  28.     QByteArray outBlock;  //分次传  
  29.     qint64 loadSize;  //每次发送数据的大小  
  30.     qint64 byteToWrite;  //剩余数据大小  
  31.     qint64 totalSize;  //文件总大小  
  32.   
  33.     int sendTimes;  //用来标记是否为第一次发送,第一次以后连接信号触发,后面的则手动调用  
  34.   
  35. private slots:  
  36.     void send();  //传送文件头信息  
  37.     void goOnSend(qint64);  //传送文件内容  
  38.     void on_openPushButton_clicked();  
  39.     void on_sendPushButton_clicked();  
  40. };  
  41.   
  42. #endif // WIDGET_H  

widget.cpp

  1. #include "widget.h"  
  2. #include "ui_widget.h"  
  3. #include <QHostAddress>  
  4. #include <QTextCodec>  
  5. #include <QFileDialog>  
  6.   
  7. Widget::Widget(QWidget *parent) :  
  8.     QWidget(parent),  
  9.     ui(new Ui::Widget)  
  10. {  
  11.     ui->setupUi(this);  
  12.   
  13.     ui->progressLabel->hide();  
  14.   
  15.     QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));  
  16.   
  17.     tcpClient = new QTcpSocket(this);  
  18.     sendTimes = 0;  
  19.   
  20.     connect(tcpClient, SIGNAL(connected()), this, SLOT(send()));  //当连接成功时,就开始传送文件  
  21.     connect(tcpClient, SIGNAL(bytesWritten(qint64)), this, SLOT(goOnSend(qint64)));  
  22.   
  23.   
  24. }  
  25.   
  26. void Widget::send()  //发送文件头信息  
  27. {  
  28.     byteToWrite = localFile->size();  //剩余数据的大小  
  29.     totalSize = localFile->size();  
  30.   
  31.     loadSize = 4*1024;  //每次发送数据的大小  
  32.   
  33.     QDataStream out(&outBlock, QIODevice::WriteOnly);  
  34.     QString currentFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);  
  35.   
  36.     out<<qint64(0)<<qint64(0)<<currentFileName;  
  37.   
  38.     totalSize += outBlock.size();  //总大小为文件大小加上文件名等信息大小  
  39.     byteToWrite += outBlock.size();  
  40.   
  41.     out.device()->seek(0);  //回到字节流起点来写好前面连个qint64,分别为总大小和文件名等信息大小  
  42.     out<<totalSize<<qint64(outBlock.size());  
  43.   
  44.     tcpClient->write(outBlock);  //将读到的文件发送到套接字  
  45.   
  46.     ui->progressLabel->show();  
  47.     ui->sendProgressBar->setMaximum(totalSize);  
  48.     ui->sendProgressBar->setValue(totalSize - byteToWrite);  
  49. }  
  50.   
  51. void Widget::goOnSend(qint64 numBytes)  //开始发送文件内容  
  52. {  
  53.     byteToWrite -= numBytes;  //剩余数据大小  
  54.     outBlock = localFile->read(qMin(byteToWrite, loadSize));  
  55.     tcpClient->write(outBlock);  
  56.   
  57.     ui->sendProgressBar->setMaximum(totalSize);  
  58.     ui->sendProgressBar->setValue(totalSize - byteToWrite);  
  59.   
  60.     if(byteToWrite == 0)  //发送完毕  
  61.         ui->sendStatusLabel->setText(tr("文件发送完毕!"));  
  62. }  
  63.   
  64. Widget::~Widget()  
  65. {  
  66.     delete ui;  
  67. }  
  68.   
  69. void Widget::on_openPushButton_clicked()  //打开文件并获取文件名(包括路径)  
  70. {  
  71.     ui->sendStatusLabel->setText(tr("正在打开文件..."));  
  72.     ui->sendProgressBar->setValue(0);  //非第一次发送  
  73.   
  74.     loadSize = 0;  
  75.     byteToWrite = 0;  
  76.     totalSize = 0;  
  77.     outBlock.clear();  
  78.   
  79.     fileName = QFileDialog::getOpenFileName(this);  
  80.     localFile = new QFile(fileName);  
  81.     localFile->open(QFile::ReadOnly);  
  82.   
  83.     ui->sendStatusLabel->setText(tr("已打开文件 %1").arg(fileName));  
  84. }  
  85.   
  86. void Widget::on_sendPushButton_clicked()  
  87. {  
  88.     if(sendTimes == 0)  //只有第一次发送的时候,是发生在连接产生信号connect时  
  89.     {  
  90.         tcpClient->connectToHost(QHostAddress("172.19.198.43"), 10000);  
  91.         sendTimes = 1;    
  92.     }  
  93.     else  
  94.         send();  //第一次发送的时候是由connectToHost出发connect信号才能调用send,第二次之后就需要调用send了  
  95.   
  96.     ui->sendStatusLabel->setText(tr("正在发送文件 %1").arg(fileName));  
  97. }  

服务端代码:

widget.h

  1. #ifndef WIDGET_H  
  2. #define WIDGET_H  
  3.   
  4. #include <QWidget>  
  5. #include <QtNetwork/QTcpServer>  
  6. #include <QtNetwork/QTcpSocket>  
  7. #include <QFile>  
  8. #include <QString>  
  9.   
  10. namespace Ui {  
  11. class Widget;  
  12. }  
  13.   
  14. class Widget : public QWidget  
  15. {  
  16.     Q_OBJECT  
  17.       
  18. public:  
  19.     explicit Widget(QWidget *parent = 0);  
  20.     ~Widget();  
  21.       
  22. private:  
  23.     Ui::Widget *ui;  
  24.   
  25.     QTcpServer *server;  
  26.     QTcpSocket *receivedSocket;  
  27.     QFile *newFile;  
  28.   
  29.     QByteArray inBlock;  
  30.     QString fileName;  
  31.     qint64 totalSize;  //总共需要发送的文件大小(文件内容&文件名信息)  
  32.     qint64 byteReceived;  //已经发送的大小  
  33.   
  34. private slots:  
  35.     void acceptConnection();  
  36.     void readClient();  
  37.     void on_pushButton_clicked();  
  38. };  
  39.   
  40. #endif // WIDGET_H  


widget.cpp

  1. #include "widget.h"  
  2. #include "ui_widget.h"  
  3. #include <QTextCodec>  
  4.   
  5. Widget::Widget(QWidget *parent) :  
  6.     QWidget(parent),  
  7.     ui(new Ui::Widget)  
  8. {  
  9.     ui->setupUi(this);  
  10.     ui->progressLabel->hide();  
  11.     QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));  
  12. }  
  13.   
  14. void Widget::acceptConnection()  
  15. {  
  16.     ui->receivedStatusLabel->setText(tr("已连接,正在准备接收文件!"));  
  17.   
  18.     receivedSocket = server->nextPendingConnection();  
  19.     connect(receivedSocket, SIGNAL(readyRead()), this, SLOT(readClient()));  
  20. }  
  21.   
  22. void Widget::readClient()  
  23. {  
  24.     ui->receivedStatusLabel->setText(tr("正在接收文件..."));  
  25.   
  26.     if(byteReceived == 0)  //才刚开始接收数据,此数据为文件信息  
  27.     {  
  28.         ui->receivedProgressBar->setValue(0);  
  29.   
  30.         QDataStream in(receivedSocket);  
  31.         in>>totalSize>>byteReceived>>fileName;  
  32.         fileName = "Files/" + fileName;  
  33.         newFile = new QFile(fileName);  
  34.         newFile->open(QFile::WriteOnly);  
  35.     }  
  36.     else  //正式读取文件内容  
  37.     {  
  38.         inBlock = receivedSocket->readAll();  
  39.   
  40.         byteReceived += inBlock.size();  
  41.         newFile->write(inBlock);  
  42.         newFile->flush();  
  43.     }  
  44.   
  45.     ui->progressLabel->show();  
  46.     ui->receivedProgressBar->setMaximum(totalSize);  
  47.     ui->receivedProgressBar->setValue(byteReceived);  
  48.   
  49.     if(byteReceived == totalSize)  
  50.     {  
  51.         ui->receivedStatusLabel->setText(tr("%1接收完成").arg(fileName));  
  52.   
  53.         inBlock.clear();  
  54.         byteReceived = 0;  
  55.         totalSize = 0;  
  56.     }  
  57. }  
  58.   
  59. Widget::~Widget()  
  60. {  
  61.     delete ui;  
  62. }  
  63.   
  64. void Widget::on_pushButton_clicked()  
  65. {  
  66.     totalSize = 0;  
  67.     byteReceived = 0;  
  68.   
  69.     server = new QTcpServer(this);  
  70.     server->listen(QHostAddress("172.19.198.43"), 10000);  
  71.   
  72.     connect(server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));  
  73.   
  74.     ui->receivedProgressBar->setValue(0);  
  75.     ui->receivedStatusLabel->setText(tr("开始监听..."));  
  76. }  


运行结果如下:



然后在文件夹中检查接收到的文件:





    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多