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站,爱编程的大丙