跟着例子学Qt--1.blockingfortuneclient

教训

之前这篇博文早已写好,不知道是忘记发布还是其他原因,写新博客时默认打开了该博客,但是没有留意直接删除内容开写,写完发布着例子学Qt2后发现跟着例子学Qt少了1~ 额,悲剧…

概述

这个例子的重点告诉我们如何利用QThread来调用系统的同步接口,Qt帮助中也提出千万不要再主线程中干同样的事,因为这将导致主线程卡死。

服务器端fortuneserver

上核心代码

tcpServer = new QTcpServer(this);
    if (!tcpServer->listen()) { // 监听随机端口(可通过tcpServer->serverPort()获取),任意ip
        QMessageBox::critical(this, tr("Fortune Server"),
                              tr("Unable to start the server: %1.")
                              .arg(tcpServer->errorString()));
        close();
        return;
    }
//! [0]
    QString ipAddress;
    QList<QHostAddress> 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(); // 通过以上方式查找到本机ip
    statusLabel->setText(tr("The server is running on\n\nIP: %1\nport: %2\n\n"
                            "Run the Fortune Client example now.")
                         .arg(ipAddress).arg(tcpServer->serverPort()));

剩下的就是响应客户端连接的代码,连接信号和槽connect(tcpServer, &QTcpServer::newConnection, this, &Server::sendFortune);,处理函数如下:

void Server::sendFortune()
{
//! [5]
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0); // 为了各种版本的兼容最好设置版本号

    out << fortunes.at(qrand() % fortunes.size());
//! [4] //! [7]

    QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
    connect(clientConnection, &QAbstractSocket::disconnected,
            clientConnection, &QObject::deleteLater);
//! [7] //! [8]

    clientConnection->write(block); // 发送一段文字后随即关闭连接。
    clientConnection->disconnectFromHost();
//! [5]
}

客户端blockingfortuneclient

重点在于FortuneThread中,完整分析cpp文件:

FortuneThread::FortuneThread(QObject *parent)
    : QThread(parent), quit(false)
{
}

//! [0]
FortuneThread::~FortuneThread()
{
    mutex.lock();
    quit = true;
    cond.wakeOne(); // 主要需要唤醒下当前线程
    mutex.unlock();
    wait(); // 如果不wait可能导致FortuneThread已经析构,而run还为结束,最后可能在run中由于访问FortuneThread已经析构的成员而导致崩溃等现象。
}
//! [0]

//! [1] //! [2]
void FortuneThread::requestNewFortune(const QString &hostName, quint16 port) // 连接服务器
{
//! [1]
    QMutexLocker locker(&mutex); // 注意加锁
    this->hostName = hostName;
    this->port = port;
//! [3]
    if (!isRunning())
        start();
    else
        cond.wakeOne();
}
//! [2] //! [3]

//! [4]
void FortuneThread::run()
{
    mutex.lock(); // 由于下面获取hostName和port,这两个参数也可能在主线程改变(requestNewFortune)故需要加锁
//! [4] //! [5]
    QString serverName = hostName;
    quint16 serverPort = port;
    mutex.unlock();
//! [5]

//! [6]
    while (!quit) {
//! [7]
        const int Timeout = 5 * 1000;

        QTcpSocket socket;
        socket.connectToHost(serverName, serverPort);
//! [6] //! [8]

        if (!socket.waitForConnected(Timeout)) { // 阻塞等待连接成功
            emit error(socket.error(), socket.errorString());
            return;
        }
//! [8] //! [11]

        QDataStream in(&socket);
        in.setVersion(QDataStream::Qt_4_0); // 同样设置了版本号
        QString fortune;
//! [11] //! [12]

        do {
            if (!socket.waitForReadyRead(Timeout)) {
                emit error(socket.error(), socket.errorString());
                return;
            }

            in.startTransaction();
            in >> fortune;
        } while (!in.commitTransaction()); // 这里通过commitTransaction可以判断发送是否发生异常,这个值得借鉴
//! [12] //! [15]

        mutex.lock();
        emit newFortune(fortune);
//! [7]

        cond.wait(&mutex); // 接收一次消息后阻塞等待条件
        serverName = hostName; // 下次仍然重设hostName及port
        serverPort = port;
        mutex.unlock();
    }
//! [15]
}

这里通过QThread代表了连接QTcpSocket等的连接,均采用同步方式,可以比较清晰地知道当前QTcpSocket的状态,比较容易管理。后面分析异步方式使用时将进行对比~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值