c++高级技巧|QT打造高效线程池异步QWebSocket 客户端

目的 我们可以使用QWebSocket得异步通信来高效传输数据,当时我们使用QWebSocket来和服务端取得视频信息流,而渲染也使用同样得界面线程,大量得包流通并且要渲染多个画面,这使得通信包来不及接收,这样,QWebSocket要使用不同得线程来接收数据包,和渲染分开,所以可以使用多个线程来将QWebSocket得异步通信放入到某个线程中,这里比较核心的是,将几个QWebSocke 放到同一个线程执行,这其实就是线程池技术。我们的示例是将两个QWebSocket放到一个线程中,学会了处理,自己稍加修改就可以做出像样的线程池了。
qt 多线程websocket Qt有两种多线程的方法,其中一种是继承QThread的run函数,
另外一种是把一个继承于QObject的类用moveToThread函数转移到一个Thread里。
Qt4.8之前都是使用继承QThread的run这种方法,Qt4.8之后,Qt官方建议使用第二种方法。
具体的使用步骤如下:
【c++高级技巧|QT打造高效线程池异步QWebSocket 客户端】1.从QObject派生一个类,将耗时的工作写在该类的槽函数中。
2.将派生类对象移动到一个QThread中,该线程需要start。(这一步使用moveToThread)
3.通过信号连接派生类的槽函数,并通过信号触发槽函数。(槽函数在子线程中执行)

#ifndef WORKER_H #define WORKER_H#include #include #include #include class Worker:public QObject { Q_OBJECT public: explicit Worker(QObject *parent=0); ~Worker(); signals: void sig_finish(); public slots: void slot_dowork(); }; #endif // WORKER_H

#include "worker.h"Worker::Worker(QObject *parent):QObject(parent) { qDebug()<<"worker()"; } Worker::~Worker() { qDebug()<<"~worker()"; } void Worker::slot_dowork() { qDebug()<< "do work,thread id = " << QThread::currentThreadId(); int i =0; while(i++<10) { qDebug()<<"this is time for:"<

其中我们的slot_dowork函数其实就是我们的线程工作函数,是通过发送信号来接收信息的,线面我们来看mainwindow
mainwindow
#ifndef MAINWINDOW_H #define MAINWINDOW_H#include #include QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACEclass MainWindow : public QMainWindow { Q_OBJECTpublic: MainWindow(QWidget *parent = nullptr); ~MainWindow(); void dowork(); private: Ui::MainWindow *ui; Worker *m_pworker; QThread *m_pthread; signals: void sig_dowork(); public slots: void slot_finish(); }; #endif // MAINWINDOW_H

mainwindow.cpp 文件

实现
#include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); m_pworker = new Worker(); m_pthread = new QThread(); m_pworker->moveToThread(m_pthread); qDebug()<< "start,thread id = " << QThread::currentThreadId(); connect(m_pthread, &QThread::finished, m_pworker, &QObject::deleteLater); connect(this,SIGNAL(sig_dowork()),m_pworker,SLOT(slot_dowork())); connect(m_pworker,SIGNAL(sig_finish()),this,SLOT(slot_finish())); }MainWindow::~MainWindow() { delete ui; m_pthread->quit(); m_pthread->wait(); }void MainWindow::dowork() { m_pthread->start(); emit sig_dowork(); }void MainWindow::slot_finish() { qDebug()<< "finish,thread id = " << QThread::currentThreadId(); }

在主函数里面启动线程得方法:
调用mainwindow的dowork,事先这些资源都已经new出来了,可以开始工作
#include "mainwindow.h"#include int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); w.dowork(); return a.exec(); }

运行结果如下图所示
c++高级技巧|QT打造高效线程池异步QWebSocket 客户端
文章图片

多个websocket 使用同一个线程 如果多个QWebSocket 使用不同得线程,创建多个以上得thread就行,问题是要分配好线程,不宜过多,不宜过少,如何做到呢,下面我们来修改一下:
我们创建一个wsclientmanager类,使得多个websocket在一个线程里运行,只要能这样做,就可以创建一个线程池,每个线程分配相应得QWebSocket。
#ifndef WSCLIENTMANAGER_H #define WSCLIENTMANAGER_H#include "worker.h" #include class wsClient:public QObject { Q_OBJECT private: QString m_name; //QThread *m_pthread = NULL; public: // void send_message(QString str, char* content); void send(QString str) { emit sig_send(str); } void start() { emit sig_start(); } public: signals: void sig_start(); void sig_send(QString str); public slots: void slot_finish() { qDebug()<< "finish,thread id = " << QThread::currentThreadId(); } }; class wsClientManager:public QObject { Q_OBJECT QMap map_ptr_Client; QMap map_ptr_work; QThread* m_pthread = NULL; public: wsClientManager(); ~wsClientManager(); int add_client(QString str); int dec_client(QString str); void start(); void send_message(QString str, QString content); }; #endif // WSCLIENTMANAGER_H

实现文件如下:
#include "wsclientmanager.h"wsClientManager::wsClientManager() { m_pthread = new QThread(); } wsClientManager::~wsClientManager() { m_pthread->quit(); m_pthread->wait(); }void wsClientManager::start() {//emit sig_dowork(); }int wsClientManager::add_client(QString str) { if(!m_pthread->isRunning()) m_pthread->start(); qDebug()<isRunning(); if(map_ptr_Client.find(str)== map_ptr_Client.end()) { wsClient* client = new wsClient(); Worker* work = new Worker(str); work->moveToThread(m_pthread); connect(client,SIGNAL(sig_start()),work,SLOT(slot_start())); connect(client,SIGNAL(sig_send(QString)),work,SLOT(send(QString))); connect(work,SIGNAL(sig_finish()),client,SLOT(slot_finish())); map_ptr_work.insert(str,work); map_ptr_Client.insert(str,client); client->start(); return 1; } return 0; } void wsClientManager::send_message(QString str, QString content) { auto iter = map_ptr_Client.find(str); if(iter!=map_ptr_Client.end()) { iter.value()->send(content); //iter->second->send(content); } else qDebug()<<"not find the name of "<; }

界面如下:
c++高级技巧|QT打造高效线程池异步QWebSocket 客户端
文章图片

在启动时先创建用户1和用户2,分别对应一个wsClient, 创建相应得Worker,并且关联,代码见addClient函数,我们点击用户1时, 发送得是用户1要发送得内容,同理用户2
事先创建用户1 qianbo1 用户2 qianbo2
void MainWindow::dowork() { m_manager.add_client("qianbo1"); m_manager.add_client("qianbo2"); //m_pthread->start(); //emit sig_dowork(); }

发送时,根据选择得用户来发送内容
void MainWindow::on_pushButton_clicked() { QString str ; if(ui->radioButton->isChecked()) { str = "qianbo1"; } else { str = "qianbo2"; }m_manager.send_message(str,ui->lineEdit->text()); }

根据qDebug 输出如下, 返回内容可以看出是不同得用户接收得
c++高级技巧|QT打造高效线程池异步QWebSocket 客户端
文章图片

那么服务端在哪里呢?我们使用nodejs来写一个非常简单得代码,一下子就有了,我们使用ws来做服务端测试
var WebSocketServer = require('ws').Server, wss = new WebSocketServer({ port: 8777 }); wss.on('connection', function (ws) { console.log('client connected'); ws.on('message', function (message) { console.log(message); ws.send("this is a test"); }); });

启动如下所示:
c++高级技巧|QT打造高效线程池异步QWebSocket 客户端
文章图片

无论谁发送过来都给一个this is a test,读者可以根据需求来做返回,也可以返回json等等,根据需求来做吧。
代码下载:下载

    推荐阅读