2016-11-09 50 views
0

我在QTcpSockets和線程結合方面遇到了一些麻煩。如何使用線程Qt網絡中的信號和插槽?

我想我誤解了關於線程的文檔,也許你可以解釋我的錯誤在哪裏。什麼部分應該進入run()方法,並且當一個信號被髮送並連接到線程類中的一個插槽時,它會在哪裏接收?在「基地」,而不是在運行(),對不對?那麼,我如何通知「正在運行」的部分,它有什麼必須做的?共享對象?怎麼樣?

我想到底存檔如下:

   +-----------------------+ 
      |  Server   | 
      | keeps some global | 
      |  objects  | 
      +--X----------------X---+ 
       X    X 
+---------------X---+   +--X----------------+ 
|     |   |     | 
| Thread for Client |   | Thread for Client | 
|  B   |   |  A   | 
| keeps local data |   | keeps local data | 
+-----------X-------+   +-----------X-------+ 
      X        X 
      X        X 
    +---------X----+    +--------X-----+ 
    | Socket  |    | Socket  | 
    +-------+---+--+    +----+---+-----+ 
     ^ |      | ^ 
      | | ---------------- | |  
      | |   Network  | |  
      | | --------------- | |  
      | |      | |  +----------------+ 
      | |      | |  |    | 
      | |      | +-------+ Client A  | 
      | |      |   |    | 
      | |      +---------->+----------------+ 
      | | 
      | |          +----------------+ 
      | +------------------------------------->+    | 
      |           | Client B  | 
      +------------------------------------------+    | 
                +----------------+ 

下面添加代碼的工作賦予「在QSocketNotifier:插座通知者不能啓動或從另一個線程禁用」。我以前的方法(這裏沒有代碼)給了「QObject:不能爲不同的線程中的父項創建子項」。我也讀過Events-Threads-Objects wiki文章(https://wiki.qt.io/Threads_Events_QObjects),但我想我有些東西混淆了。

我在這裏需要做些什麼改變,客戶端A可以發送數據到服務器,服務器更改數據並將答案傳遞給客戶端A和B(我將設置保持活動選項到實際的套接字應用程序,但出於簡化原因將其留在此處)。所有「客戶端」(他們的線程)必須能夠從服務器讀取和修改對象。這樣做的最佳方法是什麼?我想我應該在這裏使用信號和插槽,並且自動連接應該足夠好(如果所有線程都被通知一次發送數據,我可以,我的協議中有一個接收者ID,所以我可以檢查消息是否應該是調用write()之前丟棄

server.pro:

QT += network 
QT += core 

HEADERS = \ 
    server.h \ 
    clientthread.h 

SOURCES = \ 
    main.cpp \ 
    server.cpp \ 
    clientthread.cpp 

clietthead.h:

#ifndef CTHREAD_H 
#define CHREAD_H 

#include <QThread> 
#include <QTcpSocket> 

class ClientThread : public QThread { 
    Q_OBJECT 

public: 
    ClientThread(int socketDescriptor, QObject *parent); 

    void run() Q_DECL_OVERRIDE; 

    QTcpSocket * tcpSocket; 

public slots: 
    void slot_msg_answer(); 
    void slot_request_msg_FromServer(); 

signals: 
    void error(QTcpSocket::SocketError socketError); 
    void signal_for_Server_request_msg(); 

private: 
    int socketDescriptor; 

}; 

#endif 

server.h

#ifndef SERVER_H 
#define SERVER_H  
#include <QTcpServer> 

class Server : public QTcpServer { 
    Q_OBJECT 

signals: 
void signalFor_PT_msg_answert(QString); 

public: 
    Server(QObject *parent = 0); 

public slots: 
    void slot_request_msg(); 

protected: 
    void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE; 

private: 

}; 
#endif 

clientthread.cpp

#include "clientthread.h" 
#include <QTcpSocket> 
#include <QtNetwork> 

ClientThread::ClientThread(int socketDescriptor, QObject *parent) 
    : QThread(parent), socketDescriptor(socketDescriptor) { 
} 

void ClientThread::slot_request_msg_FromServer() { 
    emit signal_for_Server_request_msg(); 
    } 

void ClientThread::run() { 
    tcpSocket = new QTcpSocket(); 
    tcpSocket->setSocketOption(QAbstractSocket::KeepAliveOption, true); 
    if (!tcpSocket->setSocketDescriptor(socketDescriptor)) { 
     emit error(tcpSocket->error()); 
     return; 
    } 
    QByteArray ba ("foo"); 
    tcpSocket->write(ba); 
    tcpSocket->flush(); 
    exec(); 
} 

void ClientThread::slot_msg_answer() { 
    QByteArray ba ("bar"); 
    tcpSocket->write(ba); 
    tcpSocket->flush(); 
} 

的main.cpp

#include <QtCore> 
#include "server.h" 

int main(int argc, char *argv[]) { 
    QCoreApplication app(argc, argv); 
     Server server_; 
if (!server_.listen(QHostAddress::Any, 1234)) { 
     qDebug() << "unable to start the server"; 
     return -1; 
    } 
    return app.exec(); 
} 

server.cpp

#include "server.h" 
#include "clientthread.h" 
#include <QTimer> 

Server::Server(QObject *parent) : QTcpServer(parent) { 
} 

void Server::slot_request_msg() { 
    emit signalFor_PT_msg_answert("hello"); 
} 

void Server::incomingConnection(qintptr socketDescriptor) { 
    ClientThread *thread = new ClientThread(socketDescriptor, this); 
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); 
    connect(this, &Server::signalFor_PT_msg_answert, thread , &ClientThread::slot_msg_answer); 
    connect(thread, &ClientThread::signal_for_Server_request_msg, this, &Server::slot_request_msg); 
    thread->start(); 

    QTimer *timer = new QTimer(this); 
    connect(timer, SIGNAL(timeout()), thread, SLOT(slot_msg_answer())); 
    timer->start(2000); 
} 

所有上面的代碼是基於Qt的螺紋時運服務器的例子,這是在BSD授權:

/**************************************************************************** 
** 
** Copyright (C) 2016 The Qt Company Ltd. 
** Contact: https://www.qt.io/licensing/ 
** 
** This file is part of the examples of the Qt Toolkit. 
** 
** $QT_BEGIN_LICENSE:BSD$ 
** Commercial License Usage 
** Licensees holding valid commercial Qt licenses may use this file in 
** accordance with the commercial license agreement provided with the 
** Software or, alternatively, in accordance with the terms contained in 
** a written agreement between you and The Qt Company. For licensing terms 
** and conditions see https://www.qt.io/terms-conditions. For further 
** information use the contact form at https://www.qt.io/contact-us. 
** 
** BSD License Usage 
** Alternatively, you may use this file under the terms of the BSD license 
** as follows: 
** 
** "Redistribution and use in source and binary forms, with or without 
** modification, are permitted provided that the following conditions are 
** met: 
** * Redistributions of source code must retain the above copyright 
**  notice, this list of conditions and the following disclaimer. 
** * Redistributions in binary form must reproduce the above copyright 
**  notice, this list of conditions and the following disclaimer in 
**  the documentation and/or other materials provided with the 
**  distribution. 
** * Neither the name of The Qt Company Ltd nor the names of its 
**  contributors may be used to endorse or promote products derived 
**  from this software without specific prior written permission. 
** 
** 
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 
** 
** $QT_END_LICENSE$ 
** 
****************************************************************************/ 
+0

你有看過這個嗎? http://doc.qt.io/qt-5/threads-qobject.html特別是帶有事件循環的部分。 – Hayt

+0

是的,我讀過它,並理解它使用「exec();」在「run()」中啓動線程的「本地」事件回調,並且不在「connect(...);」上使用顯式連接類型使qt自己選擇正確的連接(默認:qtautoconnection)。 – user2567875

+0

'QThread'更像是一個線程控制器,所以除非你想改變Qt管理線程的方式,否則你最好不要繼承它。 [本文](https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/)描述了一種很好的方法, QThread'。 – TheDarkKnight

回答

1

你的線程對象有插槽。 除非Qt::DirectConnection在發出信號的線程中強制進行調用,否則將在「擁有」對象的線程環境中調用這些插槽。

您的ClientThread對象由主線程(它執行類的構造函數)「擁有」。

如果您希望在運行客戶端連接的線程中調用這些插槽,則必須將該對象移動到該線程中。

你的情況,這意味着最好的選擇是不是從QThread推導出所有,但創造你的客戶端處理程序爲QObject子類,並簡單地「搬家」到一個新的QThread情況下,看到QObject::moveToThread()

作爲獎勵點你可以測試這個沒有任何輔助線程參與。

+0

謝謝你的回答,連同TheDarkKnight上面指出的文章,我能夠更深入地理解它 – user2567875