子类化QTcpServer时,如何延迟发出newConnection()信号?

时间:2013-01-03 17:11:41

标签: c++ qt ssl qtcpserver qt-signals

我想创建一个SSL服务器,所以我将QTcpServer子类化,然后重写incomingConnection(),在其中创建QSslSocket,设置其描述符,然后调用QSslSocket::startServerEncryption。此时我需要等待发出QSslSocket::encrypted()信号,然后才能发出newConnection()信号。客户端代码会认为它使用的是QTcpSocket,但事实上它将使用安全套接字。

但是 QTcpServer在调用newConnection()之后总是发出 incomingConnection()(我查看了the source of QTcpServer):

void QTcpServerPrivate::readNotification()
{
    // .........
        q->incomingConnection(descriptor);
        QPointer<QTcpServer> that = q;
        emit q->newConnection();
    // .........
}

所以我的问题是,有没有办法阻止QTcpServer发出newConnection(),直到我准备好自己发出它?

我想要这个的原因是我希望我的类能够被用作QTcpServer的替代品,通过不知道它正在使用它的代码,所以它必须与QTcpServer完全一样:

QTcpServer* getServer(bool ssl)
{
    return ssl ? new SslServer : new QTcpServer;
}

我的SslServer类代码目前是这样的:

void SslServer::ready()
{
    QSslSocket *socket = (QSslSocket *) sender();
    addPendingConnection(socket);
    emit newConnection();
}

void SslServer::incomingConnection(int socketDescriptor)
{
    QSslSocket *serverSocket = new QSslSocket;
    if (serverSocket->setSocketDescriptor(socketDescriptor)) {
        connect(serverSocket, SIGNAL(encrypted()), this, SLOT(ready()));
        serverSocket->startServerEncryption();
    } else {
        delete serverSocket;
    }
}

2 个答案:

答案 0 :(得分:2)

这个想法在这种情况下可以起作用:重新定义newConnection子类中的QTcpServer信号。

如果这样做,与服务器实例连接的对象将不会收到信号的QTcpServer个“版本”,只会直接从您的子类中发出。

以下是概念验证:类AQTcpServerfoo是您试图“劫持”的信号,bar只是另一个(假设) )QTcpServer的信号,你不需要触摸。

class A: public QObject
{
    Q_OBJECT
    public:
        A() {};
        virtual void doit() {
            qDebug() << "A::doit";
            emit foo(1);
            emit bar(1);
        }
    signals:
        void foo(int);
        void bar(int);
};

B是您的子类。请注意,它重新定义了信号foo,但对bar没有任何作用。

class B: public A
{
    Q_OBJECT
    public:
        B() {};
        virtual void doit() {
            qDebug() << "B::doit";
            emit foo(2);
            emit bar(2);
        }
    signals:
        void foo(int);
};

C是一个潜在的客户端,从B实例连接信号/插槽,就像它与A实例一样。

class C: public QObject
{
    Q_OBJECT
    public:
        C() {
            B *b = new B;
            connect(b, SIGNAL(foo(int)), this, SLOT(foo(int)));
            connect(b, SIGNAL(bar(int)), this, SLOT(bar(int)));
            /* 1 */
            b->doit();
            /* 2 */
            b->A::doit(); // call parent class's function
        };
    public slots:
        void foo(int i) {
            qDebug() << "foo: " << i;
        }
        void bar(int i) {
            qDebug() << "bar: " << i;
        }
};

以下是构建C

的输出
B::doit       // this is /* 1 */
foo:  2 
bar:  2 
A::doit       // this is /* 2 */
bar:  1 

......别无其他。 A的{​​{1}}未与emit foo(1)的{​​{1}}广告位相关联,它永远不会到达Cfoo的{​​{1}}按预期工作,该信号未受影响。

使用该设置,您可以在课程准备就绪时发出C,用户的对象将无法接收A的信号版本。

答案 1 :(得分:1)

要成为真正的替代品,您可能需要编辑Qt的实际来源,因为您通常无法重新实现任何Private类调用。

如果您是唯一一个使用替换的人,并且您控制连接到newConnection信号的类......

只需将newConnection与您自己的广告位handleNewConnection相关联即可。当安全连接准备就绪时,发出myNewConnection并将其连接到已连接到newConnection的元素。

编辑: 经过一番挖掘后,我找到了重新连接信号的选项:

http://qt-project.org/forums/viewthread/6820

基本上,您重新实现QObject::connect,然后跟踪连接并按照您需要的方式处理它们。因此,在这种情况下,您将保留信号newConnection的所有连接的列表并将其保存在列表中,因此当您断开连接时,您可以重新连接它。请务必在重新实现结束时致电QObject::connect

走这条路线的另一个选择就是去重新路由那里的连接。当newConnection请求连接时,请将其移至myNewConnection

希望有所帮助。