Qt文档阅读笔记-Fortune Client Example实例解析
目錄
?
官方解析
實例代碼
博主增加解析
?
官方解析
Fortune Client Example
以使用QTcpSocket為例子,服務端可以配合Fortune Server或Threaded Fortune Server。
本例把QDataStream作為傳輸協議,從服務端獲取字符串。
從下面對QTcpSocket的介紹,官方給出了2種比較好的調用方法:
一種使用信號與槽達到異步的效果(個人是非常推薦這個的);
一種是使用多線程,在非GUI線程進行阻塞(估計是留給老一輩程序員,個人覺得這個程序結構性會更為通用些)。
這里的QDataStream對象是給QTcpSocket這個指針使用的。
下面new了一個QTcpSocket,并且設置了parent,這樣就不用程序員自己釋放了。
Client::Client(QWidget *parent): QDialog(parent), hostCombo(new QComboBox), portLineEdit(new QLineEdit), getFortuneButton(new QPushButton(tr("Get Fortune"))), tcpSocket(new QTcpSocket(this)), networkSession(Q_NULLPTR){setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);...in.setDevice(tcpSocket);in.setVersion(QDataStream::Qt_4_0);
這里是基于QDataStream,這個文檔里面說,要把客戶端和服務器都設置為同一個版本的流協議(這里目前先這個記著,個人覺得應該是網絡傳輸序列
化的問題,后期將會給出QDataStream的詳細講解)
當接收到數據時候會觸發QTcpSocket::readyRead()信號,當有錯誤的的時候,會觸發QTcpSocket::error()信號。
...connect(tcpSocket, &QIODevice::readyRead, this, &Client::readFortune);connect(tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),...}按下Get Fortune按鈕后會進入requestNewFortune這個槽函數:
void Client::requestNewFortune(){getFortuneButton->setEnabled(false);tcpSocket->abort();tcpSocket->connectToHost(hostCombo->currentText(),portLineEdit->text().toInt());}
使用abort()函數,終止以前的連接,使用connectToHost()來創建一個新連接。
下面來看下錯誤提示的函數:
void Client::displayError(QAbstractSocket::SocketError socketError){switch (socketError) {case QAbstractSocket::RemoteHostClosedError:break;case QAbstractSocket::HostNotFoundError:QMessageBox::information(this, tr("Fortune Client"),tr("The host was not found. Please check the ""host name and port settings."));break;case QAbstractSocket::ConnectionRefusedError:QMessageBox::information(this, tr("Fortune Client"),tr("The connection was refused by the peer. ""Make sure the fortune server is running, ""and check that the host name and port ""settings are correct."));break;default:QMessageBox::information(this, tr("Fortune Client"),tr("The following error occurred: %1.").arg(tcpSocket->errorString()));}getFortuneButton->setEnabled(true);}下面是readyRead()函數:
void Client::readFortune(){in.startTransaction();QString nextFortune;in >> nextFortune;if (!in.commitTransaction())return;if (nextFortune == currentFortune) {QTimer::singleShot(0, this, &Client::requestNewFortune);return;}currentFortune = nextFortune;statusLabel->setText(currentFortune);getFortuneButton->setEnabled(true);}
上這個代碼是重點中的重點,各種tcp面試和項目中都會用到的思路。不能想當然的認為可以一次性接收到數據包,然后展示,在網絡傳輸中,可能被
路由器或其他設備分成小包,特別是網絡比較慢的時候,可能是幾個碎片接收。但在Qt中當readyRead()信號被觸發時候,所有的碎片都被整合了,我
們直接用就可以了,但我個人覺得,至少得搞清楚Qt為開發者做了什么。至于他是怎么做到的,以后有機會再去研究吧。
這個代碼,就是在讀取沒有完整的時候進行回滾,如果讀取完整,就不回滾了。
?
實例代碼
這里我把官方代碼上的版權去掉了,方面看,在此說明下
client.h
#ifndef CLIENT_H #define CLIENT_H#include <QDialog> #include <QTcpSocket> #include <QDataStream>QT_BEGIN_NAMESPACE class QComboBox; class QLabel; class QLineEdit; class QPushButton; class QTcpSocket; class QNetworkSession; QT_END_NAMESPACE//! [0] class Client : public QDialog {Q_OBJECTpublic:explicit Client(QWidget *parent = Q_NULLPTR);private slots:void requestNewFortune();void readFortune();void displayError(QAbstractSocket::SocketError socketError);void enableGetFortuneButton();void sessionOpened();private:QComboBox *hostCombo;QLineEdit *portLineEdit;QLabel *statusLabel;QPushButton *getFortuneButton;QTcpSocket *tcpSocket;QDataStream in;QString currentFortune;QNetworkSession *networkSession; }; //! [0]#endifclient.cpp
#include <QtWidgets> #include <QtNetwork>#include "client.h"//! [0] Client::Client(QWidget *parent): QDialog(parent), hostCombo(new QComboBox), portLineEdit(new QLineEdit), getFortuneButton(new QPushButton(tr("Get Fortune"))), tcpSocket(new QTcpSocket(this)), networkSession(Q_NULLPTR) {setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); //! [0]hostCombo->setEditable(true);// find out name of this machineQString name = QHostInfo::localHostName();if (!name.isEmpty()) {hostCombo->addItem(name);QString domain = QHostInfo::localDomainName();if (!domain.isEmpty())hostCombo->addItem(name + QChar('.') + domain);}if (name != QLatin1String("localhost"))hostCombo->addItem(QString("localhost"));// find out IP addresses of this machineQList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();// add non-localhost addressesfor (int i = 0; i < ipAddressesList.size(); ++i) {if (!ipAddressesList.at(i).isLoopback())hostCombo->addItem(ipAddressesList.at(i).toString());}// add localhost addressesfor (int i = 0; i < ipAddressesList.size(); ++i) {if (ipAddressesList.at(i).isLoopback())hostCombo->addItem(ipAddressesList.at(i).toString());}portLineEdit->setValidator(new QIntValidator(1, 65535, this));QLabel *hostLabel = new QLabel(tr("&Server name:"));hostLabel->setBuddy(hostCombo);QLabel *portLabel = new QLabel(tr("S&erver port:"));portLabel->setBuddy(portLineEdit);statusLabel = new QLabel(tr("This examples requires that you run the ""Fortune Server example as well."));getFortuneButton->setDefault(true);getFortuneButton->setEnabled(false);QPushButton *quitButton = new QPushButton(tr("Quit"));QDialogButtonBox *buttonBox = new QDialogButtonBox;buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole);buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);//! [1]in.setDevice(tcpSocket);in.setVersion(QDataStream::Qt_4_0); //! [1]connect(hostCombo, &QComboBox::editTextChanged,this, &Client::enableGetFortuneButton);connect(portLineEdit, &QLineEdit::textChanged,this, &Client::enableGetFortuneButton);connect(getFortuneButton, &QAbstractButton::clicked,this, &Client::requestNewFortune);connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close); //! [2] //! [3]connect(tcpSocket, &QIODevice::readyRead, this, &Client::readFortune); //! [2] //! [4]connect(tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), //! [3]this, &Client::displayError); //! [4]QGridLayout *mainLayout = Q_NULLPTR;if (QGuiApplication::styleHints()->showIsFullScreen() || QGuiApplication::styleHints()->showIsMaximized()) {QVBoxLayout *outerVerticalLayout = new QVBoxLayout(this);outerVerticalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));QHBoxLayout *outerHorizontalLayout = new QHBoxLayout;outerHorizontalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));QGroupBox *groupBox = new QGroupBox(QGuiApplication::applicationDisplayName());mainLayout = new QGridLayout(groupBox);outerHorizontalLayout->addWidget(groupBox);outerHorizontalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));outerVerticalLayout->addLayout(outerHorizontalLayout);outerVerticalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));} else {mainLayout = new QGridLayout(this);}mainLayout->addWidget(hostLabel, 0, 0);mainLayout->addWidget(hostCombo, 0, 1);mainLayout->addWidget(portLabel, 1, 0);mainLayout->addWidget(portLineEdit, 1, 1);mainLayout->addWidget(statusLabel, 2, 0, 1, 2);mainLayout->addWidget(buttonBox, 3, 0, 1, 2);setWindowTitle(QGuiApplication::applicationDisplayName());portLineEdit->setFocus();QNetworkConfigurationManager manager;if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired) {// Get saved network configurationQSettings settings(QSettings::UserScope, QLatin1String("QtProject"));settings.beginGroup(QLatin1String("QtNetwork"));const QString id = settings.value(QLatin1String("DefaultNetworkConfiguration")).toString();settings.endGroup();// If the saved network configuration is not currently discovered use the system defaultQNetworkConfiguration config = manager.configurationFromIdentifier(id);if ((config.state() & QNetworkConfiguration::Discovered) !=QNetworkConfiguration::Discovered) {config = manager.defaultConfiguration();}networkSession = new QNetworkSession(config, this);connect(networkSession, &QNetworkSession::opened, this, &Client::sessionOpened);getFortuneButton->setEnabled(false);statusLabel->setText(tr("Opening network session."));networkSession->open();} //! [5] } //! [5]//! [6] void Client::requestNewFortune() {getFortuneButton->setEnabled(false);tcpSocket->abort(); //! [7]tcpSocket->connectToHost(hostCombo->currentText(),portLineEdit->text().toInt()); //! [7] } //! [6]//! [8] void Client::readFortune() {in.startTransaction();QString nextFortune;in >> nextFortune;if (!in.commitTransaction())return;if (nextFortune == currentFortune) {QTimer::singleShot(0, this, &Client::requestNewFortune);return;}currentFortune = nextFortune;statusLabel->setText(currentFortune);getFortuneButton->setEnabled(true); } //! [8]//! [13] void Client::displayError(QAbstractSocket::SocketError socketError) {switch (socketError) {case QAbstractSocket::RemoteHostClosedError:break;case QAbstractSocket::HostNotFoundError:QMessageBox::information(this, tr("Fortune Client"),tr("The host was not found. Please check the ""host name and port settings."));break;case QAbstractSocket::ConnectionRefusedError:QMessageBox::information(this, tr("Fortune Client"),tr("The connection was refused by the peer. ""Make sure the fortune server is running, ""and check that the host name and port ""settings are correct."));break;default:QMessageBox::information(this, tr("Fortune Client"),tr("The following error occurred: %1.").arg(tcpSocket->errorString()));}getFortuneButton->setEnabled(true); } //! [13]void Client::enableGetFortuneButton() {getFortuneButton->setEnabled((!networkSession || networkSession->isOpen()) &&!hostCombo->currentText().isEmpty() &&!portLineEdit->text().isEmpty());}void Client::sessionOpened() {// Save the used configurationQNetworkConfiguration config = networkSession->configuration();QString id;if (config.type() == QNetworkConfiguration::UserChoice)id = networkSession->sessionProperty(QLatin1String("UserChoiceConfiguration")).toString();elseid = config.identifier();QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));settings.beginGroup(QLatin1String("QtNetwork"));settings.setValue(QLatin1String("DefaultNetworkConfiguration"), id);settings.endGroup();statusLabel->setText(tr("This examples requires that you run the ""Fortune Server example as well."));enableGetFortuneButton(); }main.cpp
#include <QApplication> #include "client.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);QGuiApplication::setApplicationDisplayName(Client::tr("Fortune Client"));Client client;client.show();return app.exec(); }?
博主增加解析
本實例中還有QNetworkConfigurationManager、QNetworkConfiguration、QNetworkSession目前暫時這么理解,對網絡進行某種配置,這里要當把服務器看完之后,將會給出這些管理的詳細介紹。
?
總結
以上是生活随笔為你收集整理的Qt文档阅读笔记-Fortune Client Example实例解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++设计模式-解释器模式
- 下一篇: Leaflet笔记-Leaflet与ec