QT多线程02 继承QObject的线程
继承QObject类更加灵活。它通过 QObject::moveToThread()方法将它移到一个 QThread 线程里 执行。那么可以通过主线程发送信号去调用 QThread 线程的方法如上图的 fun4(),fun5()等等。这些方 法都是在 QThread 线程里执行的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QThread> #include <QDebug> #include <QPushButton> #include <QMutexLocker> #include <QMutex> class Worker ;class MainWindow : public QMainWindow{ Q_OBJECT public :MainWindow (QWidget *parent = nullptr );~MainWindow (); private :QPushButton *pushButton1; QPushButton *pushButton2; QThread workerThread; Worker *worker; private slots:void pushButton1Clicked () ;void pushButton2Clicked () ;void handleResults (const QString &) ;signals: void startWork (const QString &) ;}; class Worker : public QObject{ Q_OBJECT private :QMutex lock; bool isCanRun;public slots:void doWork1 (const QString ¶meter) {isCanRun = true ; while (1 ) {{ QMutexLocker locker (&lock) ;if (!isCanRun) {break ;} } QThread::sleep (2 ); emit resultReady (parameter + "doWork1 函数" ) ;} emit resultReady ("打断 doWork1 函数" ) ;} public :void stopWork () {qDebug ()<<"打断线程" <<endl;QMutexLocker locker (&lock) ;isCanRun = false ; } signals: void resultReady (const QString &result) ;}; #endif
第 51105 行,声明一个 Worker 的类继承 QObject 类,这里是参考 Qt 的 QThread 类的帮助文档的 写法。 第 6288 行,我们把耗时的工作都放于槽函数下。工人可以有不同的工作,但是每次只能去做一份。 这里不同于继承 QThread 类的线程 run(),继承 QThread 的类只有 run()在新线程里。而继承 QObject 的类,使用 moveToThread()可以把整个继承的 QObject 类移至线程里执行,所以可以有 doWork1(),doWork2…等等耗时的操作,但是这些耗时的操作都应该作为槽函数,由主线程去调用。 第 67~80 行,进入循环后使用互拆锁判断 isCanRun 变量的状态,为假即跳出 while 循环,直到 doWork1 结束。注意,虽然 doWork1 结束了,但是线程并没有退出(结束)。因为我们把这个类移到 线程里了,直到这个类被销毁。或者使用 quit()和 exit()退出线程才真正的结束!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 mainwindow.cpp #include "mainwindow.h" MainWindow::MainWindow (QWidget *parent) : QMainWindow (parent) { this ->setGeometry (0 , 0 , 800 , 480 );pushButton1 = new QPushButton (this ); pushButton2 = new QPushButton (this ); pushButton1->setGeometry (300 , 200 , 80 , 40 ); pushButton2->setGeometry (400 , 200 , 80 , 40 ); pushButton1->setText ("开启线程" ); pushButton2->setText ("打断线程" ); worker = new Worker; worker->moveToThread (&workerThread); connect (&workerThread, SIGNAL (finished ()),worker, SLOT (deleteLater ())); connect (&workerThread, SIGNAL (finished ()),&workerThread, SLOT (deleteLater ())); connect (this , SIGNAL (startWork (QString)),worker, SLOT (doWork1 (QString))); connect (worker, SIGNAL (resultReady (QString)),this , SLOT (handleResults (QString)));connect (pushButton1, SIGNAL (clicked ()),this , SLOT (pushButton1Clicked ()));connect (pushButton2, SIGNAL (clicked ()),this , SLOT (pushButton2Clicked ()));} MainWindow::~MainWindow () { worker->stopWork (); workerThread.quit (); if (workerThread.wait (2000 )) {qDebug ()<<"线程结束" <<endl;} } void MainWindow::pushButton1Clicked () {const QString str = "正在运行" ;if (!workerThread.isRunning ()) {workerThread.start (); } emit this ->startWork (str); } void MainWindow::pushButton2Clicked () {if (workerThread.isRunning ()) {worker->stopWork (); } } void MainWindow::handleResults (const QString & results) {qDebug ()<<"线程的状态:" <<results<<endl;}
第 20 行,工人类实例化。继承 QObject 的多线程类不能指定父对象。 第 24 行,工人类实例化后,工人类将自己移至 workerThread 线程里执行。 第 29~32 行,线程结束后,我们需要使用 deleteLater 来销毁 worker 对象和 workerThread对象分配 的内存。deleteLater 会确认消息循环中没有这两个线程的对象后销毁。
网络编程 1 2 3 4 5 6 7 Qt网络模块为我们提供了编写 TCP / IP 客户端和服务器的类。它提供了较低级别的类,例如代表低级网络 概念的 QTcpSocket,QTcpServer 和 QUdpSocket,以及诸如 QNetworkRequest,QNetworkReply 和 QNetworkAccessManager 之类的高级类来执行使用通用协议的网络操作。 它还提供了诸如 QNetworkConfiguration,QNetworkConfigurationManager和QNetworkSession等类,实现承载 管理。 想要在程序中使用 Qt 网络模块,我们需要在 pro 项目配置文件里增加下面的一条语句。 QT += network
获取本机的网络信息 为什么先写获取本机网络信息的内容呢?在建立网络通信之前我们至少得获取对方的 IP地址。在网络应 用中,经常需要用到本机的主机名、IP 地址、MAC 地址等网络信息,通常通在 Windows 通过调出命 令行 cmd 窗口输入 ipconfig 或者在 Linux 系统中使用 ifconfig 命令就可以查看相关信息了,在这里我 们利用 Qt 做出一个可以查询的界面和功能出来。
Qt 提供了 QHostInfo 和 QNetworkInterface 类可以用于此类信息查询。更多关于 QHostInfo和 QNetworkInterface 的相关函数可以在 Qt 的帮助文档中找到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 QT += core gui network greaterThan (QT_MAJOR_VERSION, 4 ): QT += widgetsCONFIG += c++11 # The following define makes your compiler emit warnings if you use # any Qt feature that has been marked deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain #version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the #APIs deprecated before Qt 6.0 .0 SOURCES += \ main.cpp \ mainwindow.cpp HEADERS += \ mainwindow.h # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else : unix:!android: target.path = /opt/$${TARGET}/bin!isEmpty (target.path): INSTALLS += target
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QPushButton> #include <QTextBrowser> #include <QVBoxLayout> #include <QHBoxLayout> #include <QTimer> class MainWindow : public QMainWindow{ Q_OBJECT public :MainWindow (QWidget *parent = nullptr );~MainWindow (); private :QPushButton *pushButton[2 ]; QTextBrowser *textBrowser; QWidget *hWidget; QWidget *vWidget; QHBoxLayout *hBoxLayout; QVBoxLayout *vBoxLayout; QTimer *timer; QString getHostInfo () ;private slots:void timerTimeOut () ;void showHostInfo () ;void timerStart () ;void clearHostInfo () ;}; #endif
头文件里主要是声明两个按钮和一个文本浏览框。另外还有一个定时器,声明一些槽函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 #include "mainwindow.h" #include <QtNetwork/QNetworkInterface> #include <QtNetwork/QHostInfo> #include <QThread> #include <QDebug> MainWindow::MainWindow (QWidget *parent) : QMainWindow (parent) { this ->setGeometry (0 , 0 , 800 , 480 );pushButton[0 ] = new QPushButton (); pushButton[1 ] = new QPushButton (); pushButton[0 ]->setText ("获取本机信息" ); pushButton[1 ]->setText ("清空文本信息" ); pushButton[0 ]->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); pushButton[1 ]->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); hWidget = new QWidget (); vWidget = new QWidget (); hBoxLayout = new QHBoxLayout (); vBoxLayout = new QVBoxLayout (); textBrowser = new QTextBrowser (); hBoxLayout->addWidget (pushButton[0 ]); hBoxLayout->addWidget (pushButton[1 ]); hWidget->setLayout (hBoxLayout); vBoxLayout->addWidget (textBrowser); vBoxLayout->addWidget (hWidget); vWidget->setLayout (vBoxLayout); setCentralWidget (vWidget);timer = new QTimer (); connect (pushButton[0 ], SIGNAL (clicked ()),this , SLOT (timerStart ()));connect (pushButton[1 ], SIGNAL (clicked ()),this , SLOT (clearHostInfo ()));connect (timer, SIGNAL (timeout ()),this , SLOT (timerTimeOut ()));} MainWindow::~MainWindow () { } void MainWindow::timerStart () {textBrowser->clear (); timer->start (1000 ); } void MainWindow::timerTimeOut () {showHostInfo ();timer->stop (); } QString MainWindow::getHostInfo () {QString str = "主机名称:" + QHostInfo::localHostName () + "\n" ; QList<QNetworkInterface> list = QNetworkInterface::allInterfaces (); foreach (QNetworkInterface interface, list) { str+= "网卡设备:" + interface.name () + "\n" ; str+= "MAC 地址:" + interface.hardwareAddress () + "\n" ; QList<QNetworkAddressEntry> entryList = interface.addressEntries (); foreach (QNetworkAddressEntry entry, entryList) { if (entry.ip ().protocol () == QAbstractSocket::IPv4Protocol) {str+= "IP 地址:" + entry.ip ().toString () + "\n" ; str+= "子网掩码:" + entry.netmask ().toString () + "\n" ; str+= "广播地址:" + entry.broadcast ().toString () + "\n\n" ; } } } return str;} void MainWindow::showHostInfo () {textBrowser->insertPlainText (getHostInfo ()); } void MainWindow::clearHostInfo () {if (!textBrowser->toPlainText ().isEmpty ())textBrowser->clear (); }
1 2 3 4 5 第 90~123 行,是本例最重要的代码。 第 93 行,通过 QHostInfo 的 localHostName 函数获取主机名称。 第 97~98 行,通过 QNetworkInterface::allInterfaces()获取网络接口列表 list 类存储 IP 地址子网掩码和广播地址。如果我们用 qDebug()函数打印出 list,可以发现获取了所有的网络信息。而我们要提取网络里面的网络信息使用 QNetworkAddressEntry。 第 106~107 行,使用 QNetworkAddressEntry 从 interface 接口里使用函数addressEntries(),获取所有的条目。就可以使用 QNetworkAddressEntry 的对象 entry 获取 IP地址子网掩码和广播地址。 第 110~118 行,因为获取的 entries 在一个 QNetworkInterface 下可能有两个 IP,分别是 ipv4和 ipv6。这里使用 ip().protocol()来判断协议的类型,只留下 ipv4 类型的信息。筛选信息在我们写程序常常需要的。
套接字 socket编程
socket概述
接字,用于描述IP地址和端口号
socket时链接运行在网络上的两个程序间的双向通信的端点。
通讯两端都有socket,数据在两个socket之间通过IO进行传输。
套接字是一种特殊的I/O接口,在代码内体现为特殊的文件描述符,socket是一种常见的进程间通信。
套接字主要有三种类型:
1 2 3 4 5 6 7 1、流式套接字(SOCK_STREAM) 流式套接字提供可靠的,面向连接的通信流,保证数据传输的可靠性和有序性。TCP通信使用该 套接字。 2、数据报套接字(SOCK_DGRAM) 提供不可靠、无连接的通信流,不保证可靠传输、无序。UDP通信使用该套接字。 3、原始套接字(SOCK_RAW) 允许对底层协议进行访问,功能强大但不方便。
TCP连接的建立:三次握手
TCP连接的释放:四次挥手
socket通信
服务器端将一个套接字绑定到一个特定的端口,并通过这个套接字等待和监听客户的连接请求。
客户端根据服务器所在的主机和端口号发送连接请求。
如果一切正常,服务器接受连接请求,并且获得一个新的绑定到不同端口地址的套接字。
服务器端和客户端通过读写套接字进行通讯。
流程图
服务器端 如果需要使用TCP协议创建一个服务器,需要以下步骤:
创建套接字
绑定套接字
设置监听模式
接受客户端的连接请求
接受/发送数据
断开连接
创建套接字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 socket()函数的用法: 头文件 #include <sys/types.h> #include <sys/socket.h> 函数原型 int socket(int domain, int type, int protocol); 函数参数 domain:选择通信协议,常见的协议: AF_INET:IPv4通信协议 AF_INET6:IPv6通信协议 AF_UNIX:本地通信 type:套接字类型 protocol:协议值,通常情况为0 函数返回值 成功:非负套接字文件描述符 失败:-1