在QT中使用TCP協議進行文件傳輸(可以單向循環傳輸)

大致步驟如下:

1、服務器端設置監聽套接字,開始監聽;

2、客戶端在連接成功時開始傳送文件,有connected()信號連接send()槽,send()發送文件頭信息,包括文件名、文件總大小和文件名大小等;

3、傳送完文件頭信息時開始傳送文件內容,有bytesWritten(qint64)信號連接到goOnSend(qint64)槽,前者是當想套接字寫入數據時會出發的信號,即當已經想套接字寫入數據,就繼續傳送數據,有send()傳送文件頭信息開始觸發,直到文件傳完爲止。

4、在服務器端,首先接收文件頭信息,然後讀取文件名來用新建文件的方式打開一個文件,然後讀取文件名即文件等大小信息,用來更新進度條和繼續接收數據;

5、實現循環傳輸,在客戶端,因爲第一次send()是由connected()信號觸發的,之後的每次傳送應該手動調用send();在服務器端,在有新數據到達時,會判斷是否爲頭文件,因此在每次文件傳完的時候將byteReceived重置爲0,即下一次再接收到數據的時候依據byteReceived判斷的結果就是一個新文件了。

上代碼吧,有些對象名和函數名取得不好,註釋應該可以讓大家理解了。


客戶端代碼:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include <QFile>
#include <string>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    
private:
    Ui::Widget *ui;

    QTcpSocket *tcpClient;
    QFile *localFile;
    QString fileName;  //文件名

    QByteArray outBlock;  //分次傳
    qint64 loadSize;  //每次發送數據的大小
    qint64 byteToWrite;  //剩餘數據大小
    qint64 totalSize;  //文件總大小

    int sendTimes;  //用來標記是否爲第一次發送,第一次以後連接信號觸發,後面的則手動調用

private slots:
    void send();  //傳送文件頭信息
    void goOnSend(qint64);  //傳送文件內容
    void on_openPushButton_clicked();
    void on_sendPushButton_clicked();
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
#include <QTextCodec>
#include <QFileDialog>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    ui->progressLabel->hide();

    QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));

    tcpClient = new QTcpSocket(this);
    sendTimes = 0;

    connect(tcpClient, SIGNAL(connected()), this, SLOT(send()));  //當連接成功時,就開始傳送文件
    connect(tcpClient, SIGNAL(bytesWritten(qint64)), this, SLOT(goOnSend(qint64)));


}

void Widget::send()  //發送文件頭信息
{
    byteToWrite = localFile->size();  //剩餘數據的大小
    totalSize = localFile->size();

    loadSize = 4*1024;  //每次發送數據的大小

    QDataStream out(&outBlock, QIODevice::WriteOnly);
    QString currentFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);

    out<<qint64(0)<<qint64(0)<<currentFileName;

    totalSize += outBlock.size();  //總大小爲文件大小加上文件名等信息大小
    byteToWrite += outBlock.size();

    out.device()->seek(0);  //回到字節流起點來寫好前面連個qint64,分別爲總大小和文件名等信息大小
    out<<totalSize<<qint64(outBlock.size());

    tcpClient->write(outBlock);  //將讀到的文件發送到套接字

    ui->progressLabel->show();
    ui->sendProgressBar->setMaximum(totalSize);
    ui->sendProgressBar->setValue(totalSize - byteToWrite);
}

void Widget::goOnSend(qint64 numBytes)  //開始發送文件內容
{
    byteToWrite -= numBytes;  //剩餘數據大小
    outBlock = localFile->read(qMin(byteToWrite, loadSize));
    tcpClient->write(outBlock);

    ui->sendProgressBar->setMaximum(totalSize);
    ui->sendProgressBar->setValue(totalSize - byteToWrite);

    if(byteToWrite == 0)  //發送完畢
        ui->sendStatusLabel->setText(tr("文件發送完畢!"));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_openPushButton_clicked()  //打開文件並獲取文件名(包括路徑)
{
    ui->sendStatusLabel->setText(tr("正在打開文件..."));
    ui->sendProgressBar->setValue(0);  //非第一次發送

    loadSize = 0;
    byteToWrite = 0;
    totalSize = 0;
    outBlock.clear();

    fileName = QFileDialog::getOpenFileName(this);
    localFile = new QFile(fileName);
    localFile->open(QFile::ReadOnly);

    ui->sendStatusLabel->setText(tr("已打開文件 %1").arg(fileName));
}

void Widget::on_sendPushButton_clicked()
{
    if(sendTimes == 0)  //只有第一次發送的時候,是發生在連接產生信號connect時
    {
        tcpClient->connectToHost(QHostAddress("172.19.198.43"), 10000);
        sendTimes = 1;  
    }
    else
        send();  //第一次發送的時候是由connectToHost出發connect信號才能調用send,第二次之後就需要調用send了

    ui->sendStatusLabel->setText(tr("正在發送文件 %1").arg(fileName));
}

服務端代碼:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QFile>
#include <QString>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT
    
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    
private:
    Ui::Widget *ui;

    QTcpServer *server;
    QTcpSocket *receivedSocket;
    QFile *newFile;

    QByteArray inBlock;
    QString fileName;
    qint64 totalSize;  //總共需要發送的文件大小(文件內容&文件名信息)
    qint64 byteReceived;  //已經發送的大小

private slots:
    void acceptConnection();
    void readClient();
    void on_pushButton_clicked();
};

#endif // WIDGET_H


widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QTextCodec>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->progressLabel->hide();
    QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));
}

void Widget::acceptConnection()
{
    ui->receivedStatusLabel->setText(tr("已連接,正在準備接收文件!"));

    receivedSocket = server->nextPendingConnection();
    connect(receivedSocket, SIGNAL(readyRead()), this, SLOT(readClient()));
}

void Widget::readClient()
{
    ui->receivedStatusLabel->setText(tr("正在接收文件..."));

    if(byteReceived == 0)  //纔剛開始接收數據,此數據爲文件信息
    {
        ui->receivedProgressBar->setValue(0);

        QDataStream in(receivedSocket);
        in>>totalSize>>byteReceived>>fileName;
        fileName = "Files/" + fileName;
        newFile = new QFile(fileName);
        newFile->open(QFile::WriteOnly);
    }
    else  //正式讀取文件內容
    {
        inBlock = receivedSocket->readAll();

        byteReceived += inBlock.size();
        newFile->write(inBlock);
        newFile->flush();
    }

    ui->progressLabel->show();
    ui->receivedProgressBar->setMaximum(totalSize);
    ui->receivedProgressBar->setValue(byteReceived);

    if(byteReceived == totalSize)
    {
        ui->receivedStatusLabel->setText(tr("%1接收完成").arg(fileName));

        inBlock.clear();
        byteReceived = 0;
        totalSize = 0;
    }
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    totalSize = 0;
    byteReceived = 0;

    server = new QTcpServer(this);
    server->listen(QHostAddress("172.19.198.43"), 10000);

    connect(server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));

    ui->receivedProgressBar->setValue(0);
    ui->receivedStatusLabel->setText(tr("開始監聽..."));
}


運行結果如下:



然後在文件夾中檢查接收到的文件:





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章