跟着例子学Qt--1.blockingfortuneclient

来源:互联网 发布:淘宝卖家买家秀对比 编辑:程序博客网 时间:2024/05/29 18:38

教训

之前这篇博文早已写好,不知道是忘记发布还是其他原因,写新博客时默认打开了该博客,但是没有留意直接删除内容开写,写完发布着例子学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的状态,比较容易管理。后面分析异步方式使用时将进行对比~

0 0
原创粉丝点击