【08.QT网络通信多线程】

QT网络通信多线程

QThread线程创建方法

一、写一个类继承QThread类,并重写run()函数,并在主线程中生成一个ChildThread的实例,并调用对象的start()函数
每次新建一个线程都需要继承QThread,实现一个新类,要自己进行资源管理,线程释放和删除
二、定义一个普通的QObject派生类Worker,然后将其对象move到创建的QThread类中,调用线程的start()函数
主线程如果要在子线程中运行计算必须通过发信号的方式调用,或者通过控件的信号

代码

实现文件传输功能

服务器端:
主线程:

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::WidgetClass *ui;
    QTcpServer* serv;
public slots:
    void setListen_clicked();
};
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::WidgetClass())
{
    //初始化
    ui->setupUi(this);
    ui->port->setText("8899");
    setWindowTitle("服务器");

    serv = new QTcpServer(this);
    //监听到新连接时接受新连接
    connect(serv, &QTcpServer::newConnection, this, [=]()
        {
            QTcpSocket* conntcp = serv->nextPendingConnection();
            ui->msg->append("与客户端建立连接");
            //创建子线程,并运行
            ReceiveFile* subThread = new ReceiveFile(conntcp);
            subThread->start();
            //任务完成后,回收子线程资源
            connect(subThread, &ReceiveFile::over, this, [=]() {
                subThread->exit();
                subThread->wait();
                subThread->deleteLater();
                ui->msg->append("文件接收完毕");
                QMessageBox::information(this, "文件接收", "文件接收完毕");
                });
        });
    //按钮处理
    connect(ui->setListen, &QPushButton::clicked, this, &Widget::setListen_clicked);
}

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

void Widget::setListen_clicked() {
    unsigned short port = ui->port->text().toUShort();
    serv->listen(QHostAddress::Any, port);
}

子线程接受文件

class ReceiveFile  : public QThread
{
	Q_OBJECT

public:
	ReceiveFile(QTcpSocket* tcp, QObject *parent=nullptr);
	~ReceiveFile();
protected:
	void run() override;//重写线程运行函数
private:
	QTcpSocket* tcp_;
signals:
	void over();
};
ReceiveFile::ReceiveFile(QTcpSocket* tcp, QObject* parent)
	: QThread(parent), tcp_(tcp)
{}

ReceiveFile::~ReceiveFile()
{}

void ReceiveFile::run()
{
	QFile* file = new QFile("recv.txt");
	file->open(QFile::WriteOnly);
	//就绪时读取,并写入文件
	connect(tcp_, &QTcpSocket::readyRead, this, [=]() {
		static int count = 0;
		static int total = 0;
		if (count == 0) {
			tcp_->read((char*)&total, 4);//读取文件大小
		}
		QByteArray all = tcp_->readAll();
		count += all.size();
		file->write(all);
		//写完释放资源
		if (count == total) {
			tcp_->close();
			tcp_->deleteLater();
			file->close();
			file->deleteLater();
			emit over();
		}
		});
	exec();//事件循环
}

客户端:
主线程

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::WidgetClass *ui;
public slots:
    void connect_clicked();
    void sendFile_clicked();
    void setFile_clicked();
signals:
    void startConnect(QString ip, unsigned short port);
    void sendFile(QString path);
};
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::WidgetClass())
{
    //初始化
    ui->setupUi(this);
    ui->port->setText("8899");
    ui->ip->setText("127.0.0.1");
    setWindowTitle("客户端");
    ui->progressBar->setRange(0, 100);
    ui->progressBar->setValue(0);
    //创建子线程,将运行对象放入线程中
    QThread* t = new QThread;
    SendFile* worker = new SendFile;
    worker->moveToThread(t);
    //点击按钮,子线程发送文件
    connect(this, &Widget::sendFile, worker, &SendFile::sendFile);
    //点击按钮,子线程连接服务器
    connect(this, &Widget::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);
    //按钮处理
    connect(ui->connect, &QPushButton::clicked, this, &Widget::connect_clicked);
    connect(ui->setFile, &QPushButton::clicked, this, &Widget::setFile_clicked);
    connect(ui->sendFile, &QPushButton::clicked, this, &Widget::sendFile_clicked);
    //子线程开始运行
    t->start();
}

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

void Widget::connect_clicked() {
    QString ip = ui->ip->text();
    unsigned short port = ui->port->text().toUShort();
    emit startConnect(ip, port);
}

void Widget::setFile_clicked() {
    QString path = QFileDialog::getOpenFileName();//文件对话框
    if (path.isEmpty()) {
        QMessageBox::warning(this, "打开文件", "文件路径不能为空");
        return;
    }
    ui->filePath->setText(path);
}

void Widget::sendFile_clicked() {
    emit sendFile(ui->filePath->text());
}

子线程连接服务器,发送文件

class SendFile :
    public QObject
{
    Q_OBJECT

public:
    SendFile(QObject* parent = nullptr);
    ~SendFile();
    void connectServer(QString ip, unsigned short port);
    void sendFile(QString path);
private:
    QTcpSocket* tcp;
signals:
    void connectOk();
    void gameover();
    void curPercent(int num);
};
SendFile::SendFile(QObject* parent)
    : QObject(parent) {

}

SendFile::~SendFile()
{
}

void SendFile::connectServer(QString ip, unsigned short port)
{
    tcp = new QTcpSocket;
    tcp->connectToHost(QHostAddress(ip), port);
    //连接成功,通知主线程
    connect(tcp, &QTcpSocket::connected, this, &SendFile::connectOk);
    //断开连接,回收资源,通知主线程
    connect(tcp, &QTcpSocket::disconnected, this, [=]() {
        tcp->close();
        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) {
            tcp->write((char*)&filesize,4);//先发送文件大小
        }
        QByteArray line = file.readLine();//按行读取
        num += line.size();
        int percent = (num * 100 / filesize);//百分比
        emit curPercent(percent);
        tcp->write(line);
    }
}

结果:
在这里插入图片描述

参考:
Qt多线程网络通信–b站,爱编程的大丙

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值