<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                本系列所有文章可以在這里查看[http://blog.csdn.net/cloud_castle/article/category/2123873](http://blog.csdn.net/cloud_castle/article/category/2123873) 這一章只講客戶端。這個例子與之前的Fortune Client非常相似,但QTcpsocket在這里并不是作為主類的一個成員,而是在一個單獨的線程中使用QTcpsocket的blocking?API做所有的網絡操作。因為這個操作是阻塞的,所以不能放在GUI線程中。這個客戶端是可以和Fortune Server配合使用的。文件比較多,一共有5個。 main.cpp: ~~~ #include <QApplication> #include "blockingclient.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); BlockingClient client; client.show(); return app.exec(); } ~~~ fortunethread.h: ~~~ #ifndef FORTUNETHREAD_H #define FORTUNETHREAD_H #include <QThread> #include <QMutex> // 互斥鎖,用來保護線程安全 #include <QWaitCondition> // 提供一個或多個線程的喚醒條件 //! [0] class FortuneThread : public QThread { Q_OBJECT public: FortuneThread(QObject *parent = 0); ~FortuneThread(); void requestNewFortune(const QString &hostName, quint16 port); void run(); signals: void newFortune(const QString &fortune); void error(int socketError, const QString &message); private: QString hostName; quint16 port; QMutex mutex; QWaitCondition cond; bool quit; }; //! [0] #endif ~~~ fortunethread.cpp: ~~~ #include <QtNetwork> #include "fortunethread.h" FortuneThread::FortuneThread(QObject *parent) : QThread(parent), quit(false) { } //! [0] FortuneThread::~FortuneThread() { mutex.lock(); // 設置互斥鎖為了對成員quit進行寫操作 quit = true; // 為了退出run()中的while(!quit) cond.wakeOne(); // 喚醒線程 mutex.unlock(); // 解鎖 wait(); // 等待run()返回,接著線程被析構 } //! [0] //! [1] //! [2] void FortuneThread::requestNewFortune(const QString &hostName, quint16 port) { //! [1] QMutexLocker locker(&mutex); // QMutex的一個便利類,創建時相當于mutex.lock(),被銷毀時相當于mutex.unlock(),在寫復雜代碼時很有效(局部變量的生存期) this->hostName = hostName; // 第一個hostName是線程成員變量,第二個是局部變量,是傳入參數的引用,別被名字搞混了 this->port = port; //! [3] if (!isRunning()) start(); else cond.wakeOne(); // 喚醒一個滿足等待條件的線程,這個線程即是被cond.wait()掛起的線程 } //! [2] //! [3] //! [4] void FortuneThread::run() // 多線程最重要的就是run()函數了 { mutex.lock(); // 為什么要鎖?有可能requestNewFortune同時在寫入這些數據 //! [4] //! [5] QString serverName = hostName; quint16 serverPort = port; mutex.unlock(); //! [5] //! [6] while (!quit) { // 只要quit為false就一直循環 //! [7] const int Timeout = 5 * 1000; // 5秒延時。這里用Timeout、5 * 1000 而不是 5000 使程序清晰易讀 QTcpSocket socket; // 與fortune Client不同,這里的QTcpSocket創建在棧上 socket.connectToHost(serverName, serverPort); // connectToHost還是個異步操作 //! [6] //! [8] if (!socket.waitForConnected(Timeout)) { // 在Timeout內成功建立連接返回true,默認參數為30s。這個函數就是QTcpSocket的一個Blocking API了 emit error(socket.error(), socket.errorString()); // 發送自定義信號 return; } //! [8] //! [9] while (socket.bytesAvailable() < (int)sizeof(quint16)) { // 是否連接上了但沒有數據 if (!socket.waitForReadyRead(Timeout)) { // 如果5s內沒有可供閱讀的數據則報錯(又將線程阻塞5秒) emit error(socket.error(), socket.errorString()); return; } //! [9] //! [10] } //! [10] //! [11] quint16 blockSize; // 現在開始讀取數據 QDataStream in(&socket); in.setVersion(QDataStream::Qt_4_0); in >> blockSize; // 數據長度賦予blockSize //! [11] //! [12] while (socket.bytesAvailable() < blockSize) { // 關于Tcp分段傳輸,參見Fortune Sender/Client if (!socket.waitForReadyRead(Timeout)) { // 這里主要為了等待數據達到blockSize的長度 emit error(socket.error(), socket.errorString()); return; } //! [12] //! [13] } //! [13] //! [14] mutex.lock(); QString fortune; in >> fortune; emit newFortune(fortune); // 自定義信號,newFortune被emit //! [7] //! [14] //! [15] cond.wait(&mutex); // 現在將線程掛起,掛起狀態應該在lock與unlock之間,等待cond.wakeOne()或者cond.wakeAll()喚醒 serverName = hostName; // 被喚醒后從這里開始,繼續while(!quit)循環 serverPort = port; mutex.unlock(); } //! [15] } ~~~ 呼~大頭結束了,主類就好說了,大部分代碼與Fortune Client中都是相似的,就不細講了。可以參考Qt官方demo解析集1——Fortune Client部分 blockingclient.h: ~~~ #ifndef BLOCKINGCLIENT_H #define BLOCKINGCLIENT_H #include <QWidget> #include "fortunethread.h" QT_BEGIN_NAMESPACE class QDialogButtonBox; class QLabel; class QLineEdit; class QPushButton; class QAction; QT_END_NAMESPACE //! [0] class BlockingClient : public QWidget { Q_OBJECT public: BlockingClient(QWidget *parent = 0); private slots: void requestNewFortune(); void showFortune(const QString &fortune); void displayError(int socketError, const QString &message); void enableGetFortuneButton(); private: QLabel *hostLabel; QLabel *portLabel; QLineEdit *hostLineEdit; QLineEdit *portLineEdit; QLabel *statusLabel; QPushButton *getFortuneButton; QPushButton *quitButton; QDialogButtonBox *buttonBox; FortuneThread thread; // QTcpSocket的指針沒有了,變成了一個FortuneThread類成員 QString currentFortune; }; //! [0] #endif ~~~ blockingclient.cpp: ~~~ #include <QtWidgets> #include <QtNetwork> #include <QDebug> #include "blockingclient.h" BlockingClient::BlockingClient(QWidget *parent) : QWidget(parent) { hostLabel = new QLabel(tr("&Server name:")); portLabel = new QLabel(tr("S&erver port:")); // find out which IP to connect to QString ipAddress; QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses(); // use the first non-localhost IPv4 address for (int i = 0; i < ipAddressesList.size(); ++i) { // 第(1)點 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(); hostLineEdit = new QLineEdit(ipAddress); portLineEdit = new QLineEdit; portLineEdit->setValidator(new QIntValidator(1, 65535, this)); hostLabel->setBuddy(hostLineEdit); // 上次沒提這個,因為LineEdit不方便設置快捷鍵,往往由前面Label的setBuddy綁起來。例如這里是Alt+s portLabel->setBuddy(portLineEdit); statusLabel = new QLabel(tr("This examples requires that you run the " "Fortune Server example as well.")); statusLabel->setWordWrap(true); getFortuneButton = new QPushButton(tr("Get Fortune")); getFortuneButton->setDefault(true); getFortuneButton->setEnabled(false); quitButton = new QPushButton(tr("Quit")); buttonBox = new QDialogButtonBox; buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole); buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); connect(getFortuneButton, SIGNAL(clicked()), this, SLOT(requestNewFortune())); connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); connect(hostLineEdit, SIGNAL(textChanged(QString)), this, SLOT(enableGetFortuneButton())); connect(portLineEdit, SIGNAL(textChanged(QString)), this, SLOT(enableGetFortuneButton())); //! [0] connect(&thread, SIGNAL(newFortune(QString)), // 這是線程中自定義的信號 this, SLOT(showFortune(QString))); //! [0] //! [1] connect(&thread, SIGNAL(error(int,QString)), // 同上 this, SLOT(displayError(int,QString))); //! [1] QGridLayout *mainLayout = new QGridLayout; mainLayout->addWidget(hostLabel, 0, 0); mainLayout->addWidget(hostLineEdit, 0, 1); mainLayout->addWidget(portLabel, 1, 0); mainLayout->addWidget(portLineEdit, 1, 1); mainLayout->addWidget(statusLabel, 2, 0, 1, 2); mainLayout->addWidget(buttonBox, 3, 0, 1, 2); setLayout(mainLayout); setWindowTitle(tr("Blocking Fortune Client")); portLineEdit->setFocus(); } //! [2] void BlockingClient::requestNewFortune() // 直接調用thread的requestNewFortune函數,注意線程中這個函數是需要兩個參數的 { getFortuneButton->setEnabled(false); thread.requestNewFortune(hostLineEdit->text(), portLineEdit->text().toInt()); } //! [2] //! [3] void BlockingClient::showFortune(const QString &nextFortune) { if (nextFortune == currentFortune) { requestNewFortune(); return; } //! [3] //! [4] currentFortune = nextFortune; statusLabel->setText(currentFortune); getFortuneButton->setEnabled(true); } //! [4] void BlockingClient::displayError(int socketError, const QString &message) // 整型sockError指向QAbstraction的emun量,方便使用switch跳轉 { switch (socketError) { case QAbstractSocket::HostNotFoundError: QMessageBox::information(this, tr("Blocking Fortune Client"), tr("The host was not found. Please check the " "host and port settings.")); break; case QAbstractSocket::ConnectionRefusedError: QMessageBox::information(this, tr("Blocking Fortune Client"), tr("The connection was refused by the peer. " "Make sure the fortune server is running, " "and check that the host name and port " "settings are correct.")); break; default: QMessageBox::information(this, tr("Blocking Fortune Client"), tr("The following error occurred: %1.") .arg(message)); } getFortuneButton->setEnabled(true); } void BlockingClient::enableGetFortuneButton() { bool enable(!hostLineEdit->text().isEmpty() && !portLineEdit->text().isEmpty()); // 這個表達式很有用,這里的enable不是函數名,而是一個bool型的變量名 getFortuneButton->setEnabled(enable); } ~~~ 第一點,這個程序我在自己機器上面運行的時候程序取到的都是169.254,即DHCP動態分配失敗而給機器分配的IP地址,我對網絡不是很熟,只知道這個IP地址肯定通訊不了的,事實也是這樣。通過改變(1)里面 i 的初始值可以解決這個問題,但并不是一個理想的解決方案。不過這個問題應該是個人機器問題,因為在工作電腦上調試時沒出現這個問題。姑且記錄在此吧。 好了,Blocking Fortune Client例程就說到這里啦~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看