1

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

但是QTcpServer 总是 newConnection()在调用后发出incomingConnection()(我查看了 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;
    }
}
4

3 回答 3

2

这是一个在这种情况下可行的想法:重新定义子类newConnection中的信号QTcpServer

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

这是一个概念证明:类AQTcpServer,foo是你试图“劫持”的信号,只是你不需要触摸的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是你的子类。请注意,它重新定义了 signal 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;
        }
};

这是构造 a 的输出C

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

……没有别的了。A'semit foo(1)没有连接到C'sfoo插槽,它永远不会到达C. Aemit bar(1)工作正常,该信号未受影响。

使用该设置,您可以newConnection在您的类准备就绪时发出,QTcpServer您的用户对象不会接收到该版本的信号。

于 2013-01-03T19:04:42.187 回答
1

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

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

只需连接newConnection到您自己的插槽handleNewConnection。当安全连接准备就绪时,发出myNewConnection并将其连接到将连接到的元素newConnection

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

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

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

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

希望有帮助。

于 2013-01-03T17:24:52.517 回答
1

一个肮脏的黑客将非常短暂地阻止来自 QTcpServer 的信号。由于您知道newConnection()在您返回后会立即发出,所以在您返回之前SslServer::incomingConnection()调用。this->blockSignals(true);这将阻止newConnection()调用它连接到的任何插槽。

为确保您收到后续信号,请尽快取消阻止信号。我想当控制回到事件循环时,最早可用的时间是正确的,所以 QTimer::singleShot 可以做到这一点。

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;
    }

    this -> blockSignals(true);
    QTimer::singleShot(0, this, SLOT(unblockSignals());
}

void SslServer::unblockSignals()
{
    this->blockSignals(false);
}

这样做的缺点是您将丢失在incomingConnection()和之间可以合法发出的所有信号unblockSignals()。就像我说的,这是一个肮脏的黑客。

于 2013-01-03T22:07:14.727 回答