Qt5官方Demo解析集5——Threaded Fortune Server

本系列所有文章可以在这里查看http://blog.csdn.net/cloud_castle/article/category/2123873


上篇博文谈到了QTcpSocket基于线程的同步网络客户端的实现,主要基于Waitfor...()函数。如果用多线程做服务器怎么做呢,不要紧,官网依然有Demo~

这个例子共有7个文件(不算pro,如果例子的pro文件很特别我会挑出来讨论下),部分实现与Fortune Server相同,就不重复说了。

主要是多实现了一个FortuneServer类继承自QTcpServer,为什么要写这样一个类呢?因为服务器要响应多个Client的请求,

你不能把所有Client请求的数据都写到Dialog里面吧,一是使主界面只关注GUI的实现,二也是为了复用性嘛~


例子介绍:

The Threaded Fortune Server example shows how to create a server for a simple network service that uses threads to handle requests from different clients.



main.cpp:

#include 
#include 

#include  // 使用随机函数包含的头文件

#include "dialog.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    Dialog dialog;
    dialog.show();
    qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); // 随机种子初始化
    return app.exec();
}

fortuneserver.h:

#ifndef FORTUNESERVER_H
#define FORTUNESERVER_H

#include 
#include 

//! [0]
class FortuneServer : public QTcpServer
{
    Q_OBJECT

public:
    FortuneServer(QObject *parent = 0);

protected:
    void incomingConnection(qintptr socketDescriptor); // 第(1)点

private:
    QStringList fortunes;
};
//! [0]

#endif
第一点,FortuneServer类重新实现了QTcpSocket类的incomingConnection(qintptr socketDescriptor)函数,这是个虚函数,保证了基类指针调用子类方法。qintptr是平台相关的整型,可以理解为在64位系统下 typedef qintptr qint64,在32位系统下typedef qintptr qint32。但是FortuneThread类中socketDescription参数却是int型的,为什么?因为FortuneServer类是由外部调用的,这个参数也是从外部读进来的,因此需要屏蔽系统间的差别。FortuneThread类的socketDescription是由FortuneServer给进来的,同一个平台,直接int就好了。
socketDescription是套接字的描述符,作为每个Client端的识别码。


fortuneserver.cpp:

#include "fortuneserver.h"
#include "fortunethread.h"

#include 

//! [0]
FortuneServer::FortuneServer(QObject *parent)
    : QTcpServer(parent)
{
    fortunes << tr("You've been leading a dog's life. Stay off the furniture.") // 在构造函数中将fortunes初始化
             << 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.");
}
//! [0]

//! [1]
void FortuneServer::incomingConnection(qintptr socketDescriptor) // socketDescriptor作为参数从外部接收,因此用qintptr修饰
{
    QString fortune = fortunes.at(qrand() % fortunes.size()); // qrand() % n表示在[0,n-1]取一个随机数
    FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this); // 以此socketDescription初始化FortuneThread
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); // 线程完成后删除自身的信号槽绑定
    thread->start(); // 启动线程
}
//! [1]
FortuneServer类就完成了,incomingconnection()是protected的虚函数,不能直接由QTcpSocket的对象调用的~


来看fortunethread线程类是怎么写的。fortunethread.h:

#ifndef FORTUNETHREAD_H
#define FORTUNETHREAD_H

#include 
#include 

//! [0]
class FortuneThread : public QThread
{
    Q_OBJECT

public:
    FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent);

    void run(); // 继承QThread重写run()几乎是必须的

signals:
    void error(QTcpSocket::SocketError socketError); // 自定义信号方便其他线程的特定事件处理

private:
    int socketDescriptor;
    QString text;
};
//! [0]

#endif

fortunethread.cpp:

#include "fortunethread.h"

#include 

//! [0]
FortuneThread::FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent)
    : QThread(parent), socketDescriptor(socketDescriptor), text(fortune) // 利用初始化列表对成员变量赋值
{
}
//! [0]

//! [1]
void FortuneThread::run()
{
    QTcpSocket tcpSocket; // run()函数中创建的栈对象保证了可靠的销毁。注意这个变量的依附性,当前线程变量仅在调用它的线程中有效。
//! [1] //! [2]
    if (!tcpSocket.setSocketDescriptor(socketDescriptor)) { // 相当于tcpSocket的初始化,参数是为了保证不会为同一个客户端创建多个QTcpSocket对象
        emit error(tcpSocket.error());
        return;
    }
//! [2] //! [3]

    QByteArray block; // 参见Fortune Server
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);
    out << (quint16)0;
    out << text;
    out.device()->seek(0);
    out << (quint16)(block.size() - sizeof(quint16));
//! [3] //! [4]

    tcpSocket.write(block);
    tcpSocket.disconnectFromHost(); // 这个函数是异步执行的
    tcpSocket.waitForDisconnected(); // 注意到这又是个waitFor...()函数,它会阻塞当前线程直到连接断开
}
//! [4]

dialog.h:

#ifndef DIALOG_H
#define DIALOG_H

#include 
#include "fortuneserver.h"

QT_BEGIN_NAMESPACE
class QLabel;
class QPushButton;
QT_END_NAMESPACE

class Dialog : public QWidget
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);

private:
    QLabel *statusLabel;
    QPushButton *quitButton;
    FortuneServer server; // 比较一下,与Blocking Fortune Client里的FortuneThread成员的优势
};

#endif

dialog.cpp:

#include 
#include 

#include 

#include "dialog.h"
#include "fortuneserver.h"

Dialog::Dialog(QWidget *parent)
    : QWidget(parent)
{
    statusLabel = new QLabel;
    statusLabel->setWordWrap(true);
    quitButton = new QPushButton(tr("Quit"));
    quitButton->setAutoDefault(false);

    if (!server.listen()) {                                        // 打开子线程,只用了这一句话
        QMessageBox::critical(this, tr("Threaded Fortune Server"),
                              tr("Unable to start the server: %1.")
                              .arg(server.errorString()));
        close();
        return;
    }

    QString ipAddress;
    QList ipAddressesList = QNetworkInterface::allAddresses();
    // use the first non-localhost IPv4 address
    for (int i = 0; i < ipAddressesList.size(); ++i) {
        if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
            ipAddressesList.at(i).toIPv4Address()) {
            ipAddress = ipAddressesList.at(i).toString();
            break;
        }
    }
    // if we did not find one, use IPv4 localhost
    if (ipAddress.isEmpty())
        ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
    statusLabel->setText(tr("The server is running on\n\nIP: %1\nport: %2\n\n"
                            "Run the Fortune Client example now.")
                         .arg(ipAddress).arg(server.serverPort()));

    connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));

    QHBoxLayout *buttonLayout = new QHBoxLayout;
    buttonLayout->addStretch(1);
    buttonLayout->addWidget(quitButton);
    buttonLayout->addStretch(1);

    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addWidget(statusLabel);
    mainLayout->addLayout(buttonLayout);
    setLayout(mainLayout);
    setWindowTitle(tr("Threaded Fortune Server"));
}
Fortune财富例程相关大概就是这些,回头做个总结。今天太累啦~



你可能感兴趣的:(Qt5官方Demo解析集,Qt5官方demo解析集)