QT学习:多线程运用
一、服務(wù)器端編程
首先,建立服務(wù)器端工程“TimeServer.pro”。文件代碼如下。
(1)在頭文件“dialog.h”中,定義服務(wù)器端界面類Dialog繼承自QDialog類,其具體代碼如下:
(2)在源文件“dialog.cpp”中,Dialog類的構(gòu)造函數(shù)完成了初始化界面,其具體代碼如下:
#include "dialog.h" #include <QHBoxLayout> #include <QVBoxLayout> Dialog::Dialog(QWidget *parent) : QDialog(parent) { setWindowTitle(tr("多線程時間服務(wù)器")); Label1 =new QLabel(tr("服務(wù)器端口:")); Label2 = new QLabel; quitBtn = new QPushButton(tr("退出")); QHBoxLayout *BtnLayout = new QHBoxLayout; BtnLayout->addStretch(1); BtnLayout->addWidget(quitBtn); BtnLayout->addStretch(1); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(Label1); mainLayout->addWidget(Label2); mainLayout->addLayout(BtnLayout); connect(quitBtn,SIGNAL(clicked()),this,SLOT(close())); }(3)此時運(yùn)行服務(wù)器端工程“TimeServer.pro”,界面顯示如下圖所示:
(4)在服務(wù)器端工程“TimeServer.pro”中,添加C++ Class文件“timethread.h”及“timethread.cpp”。
在頭文件“timethread.h”中,工作線程TimeThread類繼承自QThread類,實(shí)現(xiàn)TCP套接字,其具體代碼如下:
(5)在源文件“timethread.cpp”中,TimeThread類的構(gòu)造函數(shù)只是初始化了套接字描述符,其具體代碼如下:
#include "timethread.h" #include <QDateTime> #include <QByteArray> #include <QDataStream> TimeThread::TimeThread(qintptr socketDescriptor,QObject *parent) :QThread(parent),socketDescriptor(socketDescriptor) { }TimeThread::run()函數(shù)是工作線程(TimeThread)的實(shí)質(zhì)所在,當(dāng)在TimeServer::incomingConnection()函數(shù)中調(diào)用了thread->start()函數(shù)后,此虛函數(shù)開始執(zhí)行,其具體代碼如下:
void TimeThread::run() { QTcpSocket tcpSocket; //創(chuàng)建一個QTcpSocket類 if(!tcpSocket.setSocketDescriptor(socketDescriptor)) //將以上創(chuàng)建的QTcpSocket類置以從構(gòu)造函數(shù)中傳入的套接字描述符,用于向客戶端傳回服務(wù)器端的當(dāng)前時間。 { emit error(tcpSocket.error()); //如果出錯,則發(fā)出error(tcpSocket.error())信號報(bào)告錯誤。 return; } QByteArray block; QDataStream out(&block,QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_11); uint time2u = QDateTime::currentDateTime().toTime_t();//(c) out<<time2u; tcpSocket.write(block); //將獲得的當(dāng)前時間傳回客戶端 tcpSocket.disconnectFromHost(); //斷開連接 tcpSocket.waitForDisconnected(); //如果不出錯,則開始獲取當(dāng)前時間。 }(6)在服務(wù)器端工程“TimeServer.pro”中添加C++ Class文件“timeserver.h”及“timeserver.cpp”。在頭件“timeserver.h”中,實(shí)現(xiàn)了一個TCP服務(wù)器端,TimeServer類繼承自QTcpServer類,其具體代碼如下:
#include <QTcpServer> class Dialog; //服務(wù)器端的聲明 class TimeServer : public QTcpServer { Q_OBJECT public: TimeServer(QObject *parent=0); protected: void incomingConnection(qintptr socketDescriptor); //重寫此虛函數(shù)。這個函數(shù)在TCP服務(wù)器端有新的連接時被調(diào)用,其參數(shù)為所接收新連接的套接字描述符。 private: Dialog *dlg; //用于記錄創(chuàng)建這個TCP服務(wù)器端對象的父類,這里是界面指針,通過這個指針將線程發(fā)出的消息關(guān)聯(lián)到界面的槽函數(shù)中。 };(7)在源文件“timeserver.cpp”中,構(gòu)造函數(shù)只是用傳入的父類指針parent初始化私有變量dlg,其具體代碼如下:
#include "timeserver.h" #include "timethread.h" #include "dialog.h" TimeServer::TimeServer(QObject *parent):QTcpServer(parent) { dlg =(Dialog *)parent; }重寫的虛函數(shù)incomingConnection()的具體代碼如下:
void TimeServer::incomingConnection(qintptr socketDescriptor) { TimeThread *thread = new TimeThread(socketDescriptor,0); //以返回的套接字描述符socketDescriptor創(chuàng)建一個工作線程TimeThread。 connect(thread,SIGNAL(finished()),dlg,SLOT(slotShow()));//將上述創(chuàng)建的線程結(jié)束消息函數(shù)finished()關(guān)聯(lián)到槽函數(shù)slotShow()用于顯示請求計(jì)數(shù)。此操作中,因?yàn)樾盘柺强缇€程的,所以使用了排隊(duì)連接方式。 connect(thread,SIGNAL(finished()),thread,SLOT(deleteLater()), Qt::DirectConnection); //將上述創(chuàng)建的線程結(jié)束消息函數(shù)finished()關(guān)聯(lián)到線程自身的槽函數(shù) deleteLater()用于結(jié)束線程。 thread->start(); //啟動上述創(chuàng)建的線程。執(zhí)行此語句后,工作線程(TimeThread)的虛函數(shù)run()開始執(zhí)行。 }(8)在服務(wù)器端界面的頭文件“dialog.h”中添加的具體代碼如下:
class TimeServer; public slots: void slotShow(); //此槽函數(shù)用于界面上顯示的請求次數(shù) private: TimeServer *timeServer; //TCP服務(wù)器端timeServer int count; //請求次數(shù)計(jì)數(shù)器count(9)在源文件“dialog.cpp”中,添加的頭文件如下:
#include <QMessageBox> #include "timeserver.h"其中,在Dialog類的構(gòu)造函數(shù)中添加的內(nèi)容,用于啟動服務(wù)器端的網(wǎng)絡(luò)監(jiān)聽,其具體實(shí)現(xiàn)如下:
count=0; timeServer = new TimeServer(this); if(!timeServer->listen()) { QMessageBox::critical(this,tr("多線程時間服務(wù)器"), tr("無法啟動服務(wù)器:%1.").arg(timeServer->errorString())); close(); return; } Label1->setText(tr("服務(wù)器端口:%1.").arg(timeServer->serverPort()));在源文件“dialog.cpp”中,槽函數(shù)slotShow()的具體內(nèi)容如下:
void Dialog::slotShow() { Label2->setText(tr("第%1次請求完畢。").arg(++count)); //在標(biāo)簽Label2上顯示當(dāng)前的請求次數(shù),并將請求數(shù)計(jì)數(shù)count加1。注意,槽函數(shù)slotShow()雖然被多個線程激活,但調(diào)用入口只有主線程的事件循環(huán)這一個。多個線程的激活信號最終會在主線程的事件循環(huán)中排隊(duì)調(diào)用此槽函數(shù),從而保證了count變量的互斥訪問。因此,槽函數(shù)slotShow()是一個天然的臨界區(qū)。 }(10)在服務(wù)器端工程文件“TimeServer.pro”中添加如下代碼:
QT += network(11)最后運(yùn)行服務(wù)器端工程“TimeServer.pro”,結(jié)果如下圖所示。
二、客戶端編程
操作步驟如下:
(1)建立客戶端工程“TimeClient.pro”。在頭文件“timeclient.h”中,定義了客戶端界面類 TimeClient繼承自QDialog類,其具體代碼如下:
(2)在源文件“timeclient.cpp”中,TimeClient類的構(gòu)造函數(shù)完成了初始化界面,其具體代碼如下:
#include "timeclient.h" #include <QHBoxLayout> #include <QVBoxLayout> #include <QGridLayout> #include <QDataStream> #include <QMessageBox> TimeClient::TimeClient(QWidget *parent): QDialog(parent) {setWindowTitle(tr("多線程時間服務(wù)客戶端"));serverNameLabel =new QLabel(tr("服務(wù)器名:"));serverNameLineEdit = new QLineEdit("Localhost");portLabel =new QLabel(tr("端口:"));portLineEdit = new QLineEdit;QGridLayout *layout = new QGridLayout;layout->addWidget(serverNameLabel,0,0);layout->addWidget(serverNameLineEdit,0,1);layout->addWidget(portLabel,1,0);layout->addWidget(portLineEdit,1,1);dateTimeEdit = new QDateTimeEdit(this);QHBoxLayout *layout1 = new QHBoxLayout;layout1->addWidget(dateTimeEdit);stateLabel =new QLabel(tr("請首先運(yùn)行時間服務(wù)器!"));QHBoxLayout *layout2 = new QHBoxLayout;layout2->addWidget(stateLabel);getBtn = new QPushButton(tr("獲取時間"));getBtn->setDefault(true);getBtn->setEnabled(false);quitBtn = new QPushButton(tr("退出"));QHBoxLayout *layout3 = new QHBoxLayout;layout3->addStretch();layout3->addWidget(getBtn);layout3->addWidget(quitBtn);QVBoxLayout *mainLayout = new QVBoxLayout(this);mainLayout->addLayout(layout);mainLayout->addLayout(layout1);mainLayout->addLayout(layout2);mainLayout->addLayout(layout3);connect(serverNameLineEdit,SIGNAL(textChanged(QString)),this,SLOT(enableGetBtn()));connect(portLineEdit,SIGNAL(textChanged(QString)),this,SLOT(enableGetBtn()));connect(getBtn,SIGNAL(clicked()),this,SLOT(getTime()));connect(quitBtn,SIGNAL(clicked()),this,SLOT(close()));tcpSocket = new QTcpSocket(this);connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readTime()));connect(tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(showError(QAbstractSocket::SocketError)));portLineEdit->setFocus(); }在源文件“timeclient.cpp”中,enableGetBtn()函數(shù)的具體代碼如下:
void TimeClient::enableGetBtn() { getBtn->setEnabled(!serverNameLineEdit->text().isEmpty()&& !portLineEdit->text().isEmpty()); }在源文件“timeclient.cpp”中,getTime()函數(shù)的具體代碼如下:
void TimeClient::enableGetBtn() { getBtn->setEnabled(!serverNameLineEdit->text().isEmpty()&& !portLineEdit->text().isEmpty()); }在源文件“timeclient.cpp”中,readTime ()函數(shù)的具體代碼如下:
void TimeClient::readTime() { QDataStream in(tcpSocket); in.setVersion(QDataStream::Qt_5_8); if(time2u==0) { if(tcpSocket->bytesAvailable()<(int)sizeof(uint)) return; in>>time2u; } dateTimeEdit->setDateTime(QDateTime::fromTime_t(time2u)); getBtn->setEnabled(true); }在源文件“timeclient.cpp”中,showError()函數(shù)的具體代碼如下:
void TimeClient::showError(QAbstractSocket::SocketError socketError) { switch (socketError) { case QAbstractSocket::RemoteHostClosedError: break; case QAbstractSocket::HostNotFoundError: QMessageBox::information(this, tr("時間服務(wù)客戶端"), tr("主機(jī)不可達(dá)!")); break; case QAbstractSocket::ConnectionRefusedError: QMessageBox::information(this, tr("時間服務(wù)客戶端"), tr("連接被拒絕!")); break; default: QMessageBox::information(this, tr("時間服務(wù)客戶端"), tr("產(chǎn)生如下錯誤: %1.").arg(tcpSocket->errorString())); } getBtn->setEnabled(true); }(3)在客戶端工程文件“TimeClient.pro”中,添加如下代碼:
QT += network(4)運(yùn)行客戶端工程“TimeClient.pro”,顯示界面如下圖所示:
最后,同時運(yùn)行服務(wù)器和客戶端程序,單擊客戶端“獲取時間”按鈕,從服務(wù)器上獲得當(dāng)前的系統(tǒng)時間,
如下圖所示:
總結(jié)
以上是生活随笔為你收集整理的QT学习:多线程运用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QT学习:线程等待与唤醒
- 下一篇: QT学习:数据库基本概念