QT5.14.2自带Examples:Local Fortune Server/Client

概述

这是两个示例,需要配合使用。可以在本机为两个应用程序建立socket通信。
在这里插入图片描述

主要是对QLocalServer和QLocalSocket的使用。

QLocalServer

  1. 调用 listen(),让服务器开始监听本地套接字上指定关键字的连接。
  2. 每次客户端连接到服务器时,都会发出newConnection()信号。
  3. 调用nextPendingConnection()将挂起的连接接受为已连接的QLocalSocket。该函数返回一个指向QLocalSocket的指针,用于与客户端通信。

QLocalSocket

建立连接后,通过套接字收发数据。

Server 程序

Server 类定义

#ifndef SERVER_H
#define SERVER_H

#include <QDialog>

QT_BEGIN_NAMESPACE
class QLabel;
class QPushButton;
class QLocalServer;
QT_END_NAMESPACE

class Server : public QDialog
{
    Q_OBJECT

public:
    explicit Server(QWidget *parent = nullptr);

private slots:
    void sendFortune();

private:
    //QLocalServer接受传入的本地套接字连接。
    QLocalServer *server;
    ///抽取的签筒,里面放了各种签
    QStringList fortunes;
};

#endif

Server类实现

#include "server.h"

#include <QtWidgets>
#include <QtNetwork>

Server::Server(QWidget *parent)
    : QDialog(parent)
{
    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);

    server = new QLocalServer(this);
    //调用 listen()让服务器开始监听指定关键字(fortune)上的传入连接。
    if (!server->listen("fortune")) {
        QMessageBox::critical(this, tr("Local Fortune Server"),
                              tr("Unable to start the server: %1.")
                              .arg(server->errorString()));
        close();
        return;
    }

    QLabel *statusLabel = new QLabel;
    statusLabel->setWordWrap(true);
    statusLabel->setText(tr("The server is running.\n"
                            "Run the Local Fortune Client example now."));
    //把各种签放入,签筒
    fortunes << tr("You've been leading a dog's life. Stay off the furniture.")
             << tr("You've got to think about tomorrow.")
             << tr("You will be surprised by a loud noise.")
             << tr("You will feel hungry again in another hour.")
             << tr("You might have mail.")
             << tr("You cannot kill time without injuring eternity.")
             << tr("Computers are not intelligent. They only think they are.");

    QPushButton *quitButton = new QPushButton(tr("Quit"));
    //自动默认按钮 设置为false
    quitButton->setAutoDefault(false);
    connect(quitButton, &QPushButton::clicked, this, &Server::close);
    //每次客户端连接到服务器时,都会发出newConnection()信号。
    connect(server, &QLocalServer::newConnection, this, &Server::sendFortune);

    QHBoxLayout *buttonLayout = new QHBoxLayout;
    //一共三格,button在中间,两边为空。
    buttonLayout->addStretch(1);
    buttonLayout->addWidget(quitButton);
    buttonLayout->addStretch(1);

    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget(statusLabel);
    mainLayout->addLayout(buttonLayout);

    setWindowTitle(QGuiApplication::applicationDisplayName());
}

void Server::sendFortune()
{
    //const char *的高级版。
    //支持隐式共享 (copy-on-write)
    //确保以 '\0'结尾
    QByteArray block;

    //构造一个操作字节数组的数据流,
    QDataStream out(&block, QIODevice::WriteOnly);
    //如果与使用的QT版本相同可以不设置。
    out.setVersion(QDataStream::Qt_5_10);
    const int fortuneIndex = QRandomGenerator::global()->bounded(0, fortunes.size());
    const QString &message = fortunes.at(fortuneIndex);
    out << quint32(message.size());
    out << message;
    //调用nextPendingConnection()将挂起的连接接受为已连接的QLocalSocket。
    //该函数返回一个指向QLocalSocket的指针,用于与客户端通信。
    QLocalSocket *clientConnection = server->nextPendingConnection();
    //回收内存
    connect(clientConnection, &QLocalSocket::disconnected,
            clientConnection, &QLocalSocket::deleteLater);
    //block的数据与来自out数据流
    //将数据从以零结尾的8位字符字符串写入QLocalSocket缓冲。
    clientConnection->write(block);
    //QLocalSocket立即开始发送缓冲数据。
    clientConnection->flush();
    clientConnection->disconnectFromServer();
}

服务器main函数

#include <QApplication>

#include "server.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    //后面写成QApplication::tr("Local Fortune Server")也行。
    //QObject::tr,纯粹是为了加入翻译文件
    QGuiApplication::setApplicationDisplayName(Server::tr("Local Fortune Server"));
    Server server;
    server.show();
    return app.exec();
}

Clint 程序

Clint类定义

#ifndef CLIENT_H
#define CLIENT_H

#include <QDialog>
#include <QDataStream>
#include <QLocalSocket>

QT_BEGIN_NAMESPACE
class QLabel;
class QLineEdit;
class QPushButton;
QT_END_NAMESPACE

class Client : public QDialog
{
    Q_OBJECT

public:
    explicit Client(QWidget *parent = nullptr);

private slots:
    void requestNewFortune();
    void readFortune();
    void displayError(QLocalSocket::LocalSocketError socketError);
    void enableGetFortuneButton();

private:
    QLineEdit *hostLineEdit;
    QPushButton *getFortuneButton;
    QLabel *statusLabel;

    QLocalSocket *socket;
    //接收socket中的数据
    //正常接收情况下in包括:blockSize和nextFortune
    QDataStream in;
    //服务器发来的“签”的文字长度
    quint32 blockSize;

    QString currentFortune;
};

#endif

Clint类实现

#include <QtWidgets>
#include <QtNetwork>

#include "client.h"

Client::Client(QWidget *parent)
    : QDialog(parent),
      hostLineEdit(new QLineEdit("fortune")),
      getFortuneButton(new QPushButton(tr("Get Fortune"))),
      statusLabel(new QLabel(tr("This examples requires that you run the "
                                "Local Fortune Server example as well."))),
      socket(new QLocalSocket(this))
{
    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
    QLabel *hostLabel = new QLabel(tr("&Server name:"));
    hostLabel->setBuddy(hostLineEdit);

    statusLabel->setWordWrap(true);

    getFortuneButton->setDefault(true);
    QPushButton *quitButton = new QPushButton(tr("Quit"));

    QDialogButtonBox *buttonBox = new QDialogButtonBox;
    buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
    //从socket获取数据流
    in.setDevice(socket);
    in.setVersion(QDataStream::Qt_5_10);
    //本示例中下面这个connect可以忽略
    connect(hostLineEdit, &QLineEdit::textChanged,
           this, &Client::enableGetFortuneButton);

    connect(getFortuneButton, &QPushButton::clicked,
            this, &Client::requestNewFortune);
    connect(quitButton, &QPushButton::clicked, this, &Client::close);
     //每当有新的数据可从设备当前的读取通道读取时,就会发出一次readyRead信号。
    connect(socket, &QLocalSocket::readyRead, this, &Client::readFortune);
    connect(socket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error),
            this, &Client::displayError);

    QGridLayout *mainLayout = new QGridLayout(this);
    mainLayout->addWidget(hostLabel, 0, 0);
    mainLayout->addWidget(hostLineEdit, 0, 1);
    mainLayout->addWidget(statusLabel, 2, 0, 1, 2);
    mainLayout->addWidget(buttonBox, 3, 0, 1, 2);

    setWindowTitle(QGuiApplication::applicationDisplayName());
    hostLineEdit->setFocus();
}

void Client::requestNewFortune()
{
    // 本示例中,下条语句可以忽略
    getFortuneButton->setEnabled(false);
    //初始化为0
    blockSize = 0;
    //立即中止当前连接并重置套接字。
    socket->abort();
    //通过指定的关键字与Server建立连接
    socket->connectToServer(hostLineEdit->text());
}

void Client::readFortune()
{
    //为blockSize赋值
    if (blockSize == 0) {
        // QDataStream将一个quint32序列化为sizeof(quint32)个字节
        // bytesAvailable返回可供读取的字节数。
        // 目前我们需要读取的blockSize,类型为quint32

        if (socket->bytesAvailable() < (int)sizeof(quint32))
            return;
        //从socket读取quint32,socket->bytesAvailable() 的值会相应的减少。
        in >> blockSize;

    }
    if (socket->bytesAvailable() < blockSize || in.atEnd())
        return;

    QString nextFortune;
    //从socket读取字符串,socket->bytesAvailable() 的值会相应的减少。
    in >> nextFortune;
    
    //确保这次抽到的签与上次不同
    if (nextFortune == currentFortune) {
        //在给定的时间间隔后调用插槽,非常方便的静态函数
        QTimer::singleShot(0, this, &Client::requestNewFortune);
        return;
    }

    currentFortune = nextFortune;
    statusLabel->setText(currentFortune);
    getFortuneButton->setEnabled(true);
}

void Client::displayError(QLocalSocket::LocalSocketError socketError)
{
    switch (socketError) {
    case QLocalSocket::ServerNotFoundError:
        QMessageBox::information(this, tr("Local Fortune Client"),
                                 tr("The host was not found. Please make sure "
                                    "that the server is running and that the "
                                    "server name is correct."));
        break;
    case QLocalSocket::ConnectionRefusedError:
        QMessageBox::information(this, tr("Local Fortune Client"),
                                 tr("The connection was refused by the peer. "
                                    "Make sure the fortune server is running, "
                                    "and check that the server name "
                                    "is correct."));
        break;
    case QLocalSocket::PeerClosedError:
        break;
    default:
        QMessageBox::information(this, tr("Local Fortune Client"),
                                 tr("The following error occurred: %1.")
                                 .arg(socket->errorString()));
    }

    getFortuneButton->setEnabled(true);
}

void Client::enableGetFortuneButton()
{
    getFortuneButton->setEnabled(!hostLineEdit->text().isEmpty());
}

客户端 main 函数

#include <QApplication>

#include "client.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QGuiApplication::setApplicationDisplayName(Client::tr("Local Fortune Client"));
    Client client;
    client.show();
    return app.exec();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值