实现基于多线程的文件传输

Posted el3at2i6

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现基于多线程的文件传输相关的知识,希望对你有一定的参考价值。

源代码
我在代码里面都有非常详细的注释,所以就直接放上代码啦

1 客户端
头文件 mainwindow.h

ifndef MAINWINDOW_H

define MAINWINDOW_H

include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{

Q_OBJECT

public:

MainWindow(QWidget *parent = nullptr);
~MainWindow();

signals:

void startConnect(unsigned short,QString);
// 发送文件信号
void sendFile(QString path);

private slots:

void on_connectServer_clicked();

void on_selFile_clicked();

void on_sendFile_clicked();

private:

Ui::MainWindow *ui;

};

endif // MAINWINDOW_H

源文件 mainwindow.cpp

include "mainwindow.h"

include "ui_mainwindow.h"

include <QMessageBox>

include <QThread>

include "sendfile.h"

include <QFileDialog>

MainWindow::MainWindow(QWidget *parent)

: QMainWindow(parent)
, ui(new Ui::MainWindow)

{

ui->setupUi(this);

// 设置IP和端口
ui->ip->setText("127.0.0.1");
ui->port->setText("8989");

// 设置进度条
ui->progressBar->setRange(0,100);
ui->progressBar->setValue(0);

// 客户端在子线程中连接服务器
// 创建线程对象
QThread* t = new QThread;

// 创建任务对象
SendFile* worker = new SendFile;

// 将worker移动到子线程t中
worker->moveToThread(t);


// 当发送sendFile信号,让worker的sendFile函数处理(子线程)
connect(this,&MainWindow::sendFile,worker,&SendFile::sendFile);


// 通过信号,让worker开始工作
// 因为worker 已经移动到了子线程中,因此connectServer这个槽函数是在子线程中执行的
connect(this,&MainWindow::startConnect,worker,&SendFile::connectServer);

// 处理子线程发送的信号
// 连接成功
connect(worker,&SendFile::connectOK,this,[=](){
    QMessageBox::information(this,"连接服务器","已经成功的连接了服务器,恭喜!");
});

// 断开连接
connect(worker,&SendFile::gameover,this,[=](){
    // 资源释放
    t->quit();
    t->wait();
    worker->deleteLater();
    t->deleteLater();
});

connect(worker,&SendFile::curPercent,ui->progressBar,&QProgressBar::setValue);
// 启动线程
t->start();

}

MainWindow::~MainWindow()
{

delete ui;

}

void MainWindow::on_connectServer_clicked()
{

QString ip = ui->ip->text();
unsigned short port = ui->port->text().toUShort();
emit startConnect(port,ip);

}

void MainWindow::on_selFile_clicked()
{

QString path = QFileDialog::getSaveFileName();
// 判断路径是否为空
if(path.isEmpty())
{
    QMessageBox::warning(this,"打开文件","选择的文件路径不能为空");
    return;
}
ui->filePath->setText(path);

}

void MainWindow::on_sendFile_clicked()
{

// 发送文件信号
emit sendFile(ui->filePath->text());

}

头文件 Send File.h

ifndef SENDFILE_H

define SENDFILE_H

include <QObject>

include <QTcpSocket>

class SendFile : public QObject
{

Q_OBJECT

public:

explicit SendFile(QObject *parent = nullptr);

// 连接服务器
void connectServer(unsigned short port,QString ip);

// 发送文件
void sendFile(QString path);

signals:

// 通知主线程连接成功
void connectOK();
// 通知主线程连接成功
void gameover();
// 通知主线程发送文件进度百分比
void curPercent(int num);

private:

QTcpSocket* m_tcp;

};

endif // SENDFILE_H

源文件SendFile.cpp

include "sendfile.h"

include <QFile>

include <QHostAddress>

include <QFileInfo>

SendFile::SendFile(QObject* parent) : QObject(parent)
{

}

void SendFile::connectServer(unsigned short port, QString ip)
{

m_tcp = new QTcpSocket;
m_tcp->connectToHost(QHostAddress(ip),port);

// 通知主线程连接成功
connect(m_tcp,&QTcpSocket::connected,this,&SendFile::connectOK);

// 通知主线程断开连接
connect(m_tcp,&QTcpSocket::disconnected,this,[=](){
    // 断开连接,释放资源
    m_tcp->close();
    m_tcp->deleteLater();
    emit gameover();
});

}

void SendFile::sendFile(QString path)
{

QFile file(path);
// 获取文件信息
QFileInfo info(path);
int fileSize = info.size();

file.open(QFile::ReadOnly);

// 一行一行的读文件
while(!file.atEnd()){
    static int num = 0;
    // 为了让服务器端知道什么时候停止接收,所以得发送文件的大小
    if(num ==0){
        m_tcp->write((char*)&fileSize,4);
    }

    QByteArray line = file.readLine();
    // 计算百分比,发给主线程
    num +=line.size();
    int percent =(num*100/fileSize);
    emit curPercent(percent);
    m_tcp->write(line);


}

}

3.2 服务端
头文件mainwindow.h

ifndef MAINWINDOW_H

define MAINWINDOW_H

include <QMainWindow>

include <QTcpServer>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{

Q_OBJECT

public:

MainWindow(QWidget *parent = nullptr);
~MainWindow();

private slots:

void on_setListen_clicked();

private:

Ui::MainWindow *ui;
QTcpServer* m_s;

};

endif // MAINWINDOW_H

源文件maindow.cpp

include "mainwindow.h"

include "ui_mainwindow.h"

include <QMessageBox>

include <QTcpSocket>

include "recvfile.h"

MainWindow::MainWindow(QWidget *parent)

: QMainWindow(parent)
, ui(new Ui::MainWindow)

{

ui->setupUi(this);

m_s = new QTcpServer(this);


connect(m_s,&QTcpServer::newConnection,this,[=](){
    QTcpSocket* tcp = m_s->nextPendingConnection();
    // 创建子线程,tcp通过参数传递
    RecvFile* subThread = new RecvFile(tcp);
    subThread->start();
    connect(subThread,&RecvFile::over,this,[=](){
       subThread->exit();
       subThread->wait();
       subThread->deleteLater();
       QMessageBox::information(this,"文件接受","文件接收完毕!!!");
    });
});

}

MainWindow::~MainWindow()
{

delete ui;

}

void MainWindow::on_setListen_clicked()
{

unsigned short port = ui->port->text().toUShort();
m_s->listen(QHostAddress::Any,port);

}

头文件recvfile.h

ifndef RECVFILE_H

define RECVFILE_H

include <QThread>

include <QTcpSocket>

class RecvFile : public QThread
{

Q_OBJECT

public:

explicit RecvFile(QTcpSocket* tcp,QObject *parent = nullptr);

protected:

void run() override;

private:

QTcpSocket* m_tcp;

signals:

void over();

};

endif // RECVFILE_H

源文件recvfile.cpp

include "recvfile.h"

include <QFile>

RecvFile::RecvFile(QTcpSocket tcp,QObject parent) : QThread(parent)
{

m_tcp = tcp;

}

void RecvFile::run()
{

QFile* file = new QFile("recv.txt");
file->open(QFile::WriteOnly);

// 接收数据
connect(m_tcp,&QTcpSocket::readyRead,this,[=](){
    static int count = 0;
    static int total = 0;
    if(count == 0){
        m_tcp->read((char*)&total,4);
    }
    // 读出剩余数据
    QByteArray all = m_tcp->readAll();
    count += all.size();
    file->write(all);

    if(count == total){
        m_tcp->close();
        m_tcp->deleteLater();
        file->close();
        file->deleteLater();
        emit over();
    }
});

// 进入事件循环
exec();

}

以上是关于实现基于多线程的文件传输的主要内容,如果未能解决你的问题,请参考以下文章

用java多线程实现服务器与客户端之间的文件传输的代码!!!急!!!!

Java如何实现多线程传输文件,就像迅雷下载一样,开十多个线程分段传送字节流?

JAVA 基于TCP协议的一对一,一对多文件传输实现

自己开发的在线视频下载工具,基于Java多线程

基于多线程多用户的FTP服务器与客户端功能实现

java socket多文件传输问题