如何在线程Qt网络中使用信号和插槽?

时间:2016-11-09 16:12:38

标签: qt qt5 qthread qtcpsocket

我在使用QTcpSocket和线程时遇到了一些问题。

我想我误解了有关Threads的文档,也许你可以解释我的错误所在。什么部分应该进入run()方法,当发出信号并连接到线程类的插槽时,它在哪里接收?在" base"中,不在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     |
          +------------------------------------------+                |
                                                     +----------------+

下面添加的代码工作提供" QSocketNotifie r:无法从另一个线程启用或禁用套接字通知程序"。我之前的方法(这里没有代码)给出了#34; QObject:无法为不同线程中的父级创建子级。"。我也读过了Events-Threads-Objects wiki文章(https://wiki.qt.io/Threads_Events_QObjects),但我想我有些混淆了。

我需要在此处更改,客户端A可以将数据发送到服务器,服务器更改数据并将答案传递给客户端A和B(我将在实际应用程序中为套接字设置keep-alive选项,但出于简化原因将其留在这里)。所有"客户" (他们的线程)必须能够从服务器读取和修改对象。这样做的最佳方法是什么?我想我应该在这里使用信号和插槽,并且autoconnection应该足够好(如果通知所有线程立即发送数据,我很好,我的协议中有reciever个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$
**
****************************************************************************/

1 个答案:

答案 0 :(得分:1)

您的线程对象有插槽。 那些插槽将在&#34;拥有&#34;的线程的上下文中被调用。除非Qt::DirectConnection强制调用发出信号的线程中的对象。

您的ClientThread个对象拥有&#34;拥有&#34;由主threadm(它执行类的构造函数)。

如果要在运行客户端连接的线程中调用这些槽,则必须将对象移动到该线程中。

这意味着在您的情况下,最好的选择是不要从QThread派生,而是将您的客户端处理程序创建为QObject子类,并简单地&#34;移动&#34;它是一个新的QThread个实例,请参阅QObject::moveToThread()

作为奖励点,您可以在不涉及任何辅助线程的情况下对此进行测试。