目录

官方解析

实例代码

博主增加解析


官方解析

Fortune Client Example
以使用QTcpSocket为例子,服务端可以配合Fortune Server或Threaded Fortune Server。
本例把QDataStream作为传输协议,从服务端获取字符串。
从下面对QTcpSocket的介绍,官方给出了2种比较好的调用方法:
一种使用信号与槽达到异步的效果(个人是非常推荐这个的);
一种是使用多线程,在非GUI线程进行阻塞(估计是留给老一辈程序员,个人觉得这个程序结构性会更为通用些)。

  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;};

这里的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为开发者做了什么。至于他是怎么做到的,以后有机会再去研究吧。

QString nextFortune;
in >> nextFortune;if (!in.commitTransaction())return;

这个代码,就是在读取没有完整的时候进行回滚,如果读取完整,就不回滚了。

实例代码

这里我把官方代码上的版权去掉了,方面看,在此说明下

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]#endif

client.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实例解析相关推荐

  1. Qt文档阅读笔记-Multiple Inheritance Example 实例解析及Automatic Connections解析

    目录 Multiple Inheritance Example 实例解析 Automatic Connections解析 Multiple Inheritance Example 实例解析 这个实例很 ...

  2. Qt文档阅读笔记-QML Canvas的官方解析及实例

    目录 官方解析 博主例子 官方解析 Canvas可以用于画直线或曲线,简单或复杂的形状,图形,图片,并且他能加文字,颜色,阴影,和颜色梯度,和其他的装饰,可以进行低像素操作.Canvas可以保存成图像 ...

  3. Qt文档阅读笔记-DTLS client解析

    此篇博文讲解了DTLS客户端的编写 注意:DTLS客户端需要结合DTLS服务端一起跑才有效果. 这里使用DTLS客户端使用少量的连接可以和一个或多个DTLS服务端进行通信.DtlsAssociatio ...

  4. Qt文档阅读笔记-Simple Anchor Layout Example解析

    这个例子展示了锚布局在视图场景中的使用. 此篇例子其实就是教QGraphicsAnchorLayout类的使用. 首先创建了QGraphicScene(场景),3个widgets(a,b,c),以及一 ...

  5. Qt文档阅读笔记-QtConcurrent Progress Dialog Example解析

    这篇展示了如何监听任务的进度. QtConcurrent Progress Dialog使用QFutrueWathcer类去监听任务进程进展. 代码如下: progressdialog.pro QT ...

  6. Qt文档阅读笔记-FileDialog QML Type官方解析与实例

    目录 官方解析 博主例子 官方解析 FileDialog是基于文件的选择器,可以选择文件或文件夹,创建文件,这个Dialog初始化是不可见的,得需要设置他为visible或调用open()即可. 下面 ...

  7. Qt文档阅读笔记-Label QML Type官方解析及实例

    目录 官方解析 博主例子 官方解析 Label扩展了父类Text中的styling和font.Label同样是有可视化的background项. Label {text: "Label&qu ...

  8. Qt文档阅读笔记-TextEdit QML Type官方解析及实例

    目录 官方解析 博主栗子 官方解析 TextEdit展示了一个可编辑的一块,是有格式的文本. 他同样能展示普通文本和富文本: TextEdit {width: 240text: "<b ...

  9. Qt文档阅读笔记-Text QML Type官方解析及实例

    目录 官方解析 博主例子 官方解析 Text能够展示纯文本和富文本.举个例子,红色文本以及指定的字体和大小 Text {text: "Hello World!"font.famil ...

最新文章

  1. 精准广告系统架构调研
  2. ASP.NET State Service
  3. 【C++】decltype作用探究,unsigned与signed混淆问题
  4. 使用 Go 实现生产者和消费者,Kafka 正式升级到 3.0!
  5. H.264/AVC 标准中CAVLC 和CABAC 熵编码算法研究
  6. Jenkins 与 Kubernetes 的 CI 与 CD Git + Maven + Docker+Kubectl
  7. DataTable操作相关实例
  8. opencv 智能答卷识别系统(一)
  9. 动态RAM的集中刷新、分散刷新、异步刷新
  10. 计算机中的基础元素,数据结构基础
  11. kibana界面汉化
  12. 5双机配置_CentOS 7 高可用双机热备实现
  13. My SQL中show命令--MySQL中帮助查看
  14. 对setTimeout()第一个参数是字串的深入理解以及eval函数的理解
  15. 重置winsock目录解决不能上网的问题
  16. MapReduce概述 —— Hadoop权威指南2
  17. C/C++ abs 函数 - C语言零基础入门教程
  18. bat自动输入密码_如何给电脑文件夹设置密码?一学就会
  19. fromPCAtoprincipalcurvetoprincipalgraph_拔剑-浆糊的传说_新浪博客
  20. 使用ps命令结束相应进程

热门文章

  1. 近找到了一个免费的python教程,两周学会了python开发【内附学习视频】
  2. WEB运用程序如何实现高效可维护?
  3. ykcchf 2013 v2.1101 最新版下载
  4. 一个简单的录音软件程序代码【C++】
  5. 如果谁和飞鸽传书讨论这两个问题
  6. 实话!程序员大都不喜欢拉帮结派
  7. 关于CompleteWithAppPath函数,CompleteWithAppPath(aFileName)
  8. 飞鸽传书10048错误的解决
  9. 莱比锡爆料:《星际争霸2》估计明年也没戏
  10. 跨进程实现在Tree中快速定位节点