之前这篇博文早已写好,不知道是忘记发布还是其他原因,写新博客时默认打开了该博客,但是没有留意直接删除内容开写,写完发布着例子学Qt2后发现跟着例子学Qt少了1~ 额,悲剧…
这个例子的重点告诉我们如何利用QThread来调用系统的同步接口,Qt帮助中也提出千万不要再主线程中干同样的事,因为这将导致主线程卡死。
上核心代码
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);
,处理函数如下:
重点在于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的状态,比较容易管理。后面分析异步方式使用时将进行对比~
新闻热点
疑难解答