首先,新建一个项目,我命名为了SendFileClient

首先我们要在pro文件中 代码第一行加入network的后缀
 
一、窗口搭建


如图所示,在第一个QWidget中让客户端输入IP,端口号 连接服务器
第二个Qwidget 设置一个LineEdit,供客户端选择要发送的文件
在下面设置一个 progressBar ,用来显示文件上传的进度
在最下面设置一个按钮点击发送文件。
二、创建一个新类
既然运用到了多线程,我们将连接服务器,选择文件,发送文件的任务交给子线程,主线程去管理进度条的任务

右键项目add new新建一个类

继承Qobject,这里我取名为sendfile
在sendfile.h中定义两个函数用来连接服务器,发送文件

mainwindow的操作
连接服务器

首先在mainwindow的构造函数中 初始化 ip ,端口号 ,进度条 的值
我要将发送文件,连接服务器在子线程中操作的话,我们就要先创建一个线程对象和任务对象,将任务对象通过moveToThread函数移动到创建出来的线程中去工作。

接下来我们实现 连接服务器的操作
右键ui中的连接服务器转到槽函数的clicked()

 函数中的内容
要提前在mainwindow.h在定义一个信号,提醒worker对象中的connectSever开始执行

 
在上面的构造函数中,用connect如果收到startConnect的信号,就去执行woker中的connectSever函数
然后启动子线程用start()
看到这先去看sendfile操作再回来看下面的 1

 这时主线程收到了子线程发来的connectOK,就知道成功连接到了服务器
 如果收到了gameover,就知道服务器断开了,进行资源释放
发送文件

给选择文件添加一个处理函数


发送文件同样


提前在mainwindow.h中添加好这个信号

在mainwindow的构造函数中添加一个connect表示如果我收到了发来的sendfile信号,就去完成sendfile类中的send函数
这里跳转到sendfile后面观看 2
更新进度条
在构造函数中添加connect,如果收到curpencent信号,将信号中传来的pencent去设置进度条的值
![]()
mainwindow到这也结束了
sendfile的操作
1

现在sendfile.h文件中定义一个QTcpSocket的套接字,用于通信

完善一下connectSever操作

提前在sendfile.h中先定义好信号;
第一次看到这可以回去看了
2

将文件发送给服务器,将文件传输进度通过信号告诉主线程

提前添加一个curpencent的信号
这时回到主窗口更新进度条,senfile到这一步就完成了
完整代码
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include"sendfile.h"
#include<QThread>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
    Q_OBJECT
signals:
    void startconnect(unsigned short ,QString);
    void sendfile(QString path);
public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private slots:
    void on_pushButton_clicked();
    void on_selFile_clicked();
    void on_sendFile_clicked();
private:
    Ui::MainWindow *ui;
    QThread *t;
    sendFile * work;
};
#endif // MAINWINDOW_H
 
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QMessageBox>
#include<QFileDialog>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->ip->setText("127.0.0.1");
    ui->port->setText("8899");
    ui->progressBar->setRange(0,100);
    ui->progressBar->setValue(0);
    t = new QThread;
    work = new sendFile; //实例化一个sendfile类
    work->moveToThread(t);//将实例化的类要执行的程序移动到线程里去执行
    connect(this,&MainWindow::sendfile,work,&sendFile::send);
    connect(this,&MainWindow::startconnect,work,&sendFile::connectSever);//如果点击连接按钮,这里会接收到一个startconnct的槽函数,然后去执行work中的connctSever操作
    connect(work,&sendFile::connectOK,this,[=](){ //如果connctsever连接成功,会发来一个connctok的槽函数
        QMessageBox::information(this,"连接服务器","连接服务器成功");
    });
    connect(work,&sendFile::gameover,this,[=](){  //如果断开连接会发来一个gameover的槽函数
        t->quit();
        t->wait();
        t->deleteLater();
        work->deleteLater();
    });
    connect(work,&sendFile::curpencent,ui->progressBar,&QProgressBar::setValue);
    t->start();
}
MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::on_pushButton_clicked()
{
    unsigned short port = ui->port->text().toUShort();//将ui中的端口号转换为short类型取出保存
    QString ip = ui->ip->text().toUtf8();//将对象转换为UTF-8编码的字节序列,存储在QString对象中,
    emit startconnect(port,ip);//发送一个开始连接的信号
}
void MainWindow::on_selFile_clicked()
{
    QString path = QFileDialog::getOpenFileName();//通过此方法可以得到某个磁盘文件的绝对路径
    if(path.isEmpty())
    {
        QMessageBox::warning(this,"选择文件","选择的文件不能为空");//如果选择文件为空弹出一个警告对话窗
        return;
    }
    ui->filePath->setText(path);//将选择好的文件设置给文本框
}
void MainWindow::on_sendFile_clicked()
{
    emit sendfile(ui->filePath->text());//因为发送文件的操作是在子线程中做的,所以发送一个信号告诉子线程文件路径名
}
 
sendfile.h
#ifndef SENDFILE_H
#define SENDFILE_H
#include <QObject>
#include<QTcpSocket>
#include <QHostAddress>
class sendFile : public QObject
{
    Q_OBJECT
public:
    explicit sendFile(QObject *parent = nullptr);
    void connectSever(unsigned short port, QString ip);//连接服务器
    void send(QString path);//发送文件
signals:
    void connectOK();
    void gameover();
    void curpencent(int );
private:
    QTcpSocket *tcp;
};
#endif // SENDFILE_H
 
sendfile.cpp
#include "sendfile.h"
#include <QFile>
#include <QFileInfo>
#include <QHostAddress>
sendFile::sendFile(QObject *parent) : QObject(parent)
{
}
void sendFile::connectSever(unsigned short port, QString ip)
{
    tcp = new QTcpSocket;//实例化套接字
    tcp->connectToHost(QHostAddress(ip),port);//通过给定的ip,port连接服务器
    connect(tcp,&QTcpSocket::connected,this,&sendFile::connectOK);//如果连接成功就发送一个connectOK的信号告诉主线程
    connect(tcp,&QTcpSocket::disconnected,[=](){//如果断开连接就关闭套接字并释放,发送gameover的信号给主线程
        tcp->close();
        tcp->deleteLater();
        emit  gameover();
    });
}
void sendFile::send(QString path)
{
    // 创建一个QFile对象,将要发送的文件与该对象关联
    QFile file(path);
    QFileInfo info(path);// 创建一个QFileInfo对象,用于获取文件的元信息,如大小等
    file.open(QFile::ReadOnly); // 打开文件为只读模式
    int fileSize = info.size();// 获取文件的大小
    while(!file.atEnd())// 当文件未读到末尾时,循环读取文件
    {
        static int num = 0; // 定义一个静态变量num,用于累计已读取的字节数
        if(num == 0)// 如果是第一次读取,则发送文件的大小信息
        {
            tcp->write((char *)&fileSize, 4);// 将文件大小的前4个字节写入tcp套接字,以通知接收端文件的大小
            num = 4;// 将静态变量num设置为4,因为已经发送了文件大小的前4个字节
        }
        QByteArray line = file.readLine(); // 读取一行数据
        num += line.size();// 将读取的字节数累加到num
        int percent = (num * 100 / fileSize); // 计算已读取的字节数占总字节数的百分比
        tcp->write(line);// 将读取的数据行写入tcp套接字
        emit curpencent(percent);// 发射信号,通知界面文件传输的进度
    }
}
 
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
 
                


















