Я хочу создать сервер 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;
}
}
Вот идея, которая может работать в этом случае: переопределить newConnection
сигнал в вашем QTcpServer
подкласс.
Если вы сделаете это, объекты, которые связаны с экземпляром вашего сервера, не получат QTcpServer
«Версия» сигнала, только та, которую вы излучаете прямо из вашего подкласса.
Вот подтверждение концепции: класс A
это QTcpServer
, 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
ваш подкласс Обратите внимание, что он переопределяет сигнал 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
«s emit foo(1)
не связан с C
«s foo
слот, он никогда не прибудет в C
, A
«s emit bar(1)
сработало как положено, этот сигнал не тронут.
С этой настройкой вы можете излучать newConnection
когда твой класс готов, QTcpServer
версия сигнала не будет получена объектами вашего пользователя.
Чтобы быть настоящим отбрасыванием замены, вам, вероятно, понадобится отредактировать фактический источник Qt, потому что вы обычно не можете переопределить любой Private
Классовые звонки.
Если вы единственный, кто использует замену, и вы контролируете классы, которые подключаются к newConnection
сигнал…
Просто подключите newConnection
в свой слот handleNewConnection
, Когда безопасное соединение будет готово, выделите myNewConnection
и соединить это с элементами, которые были бы связаны с newConnection
,
РЕДАКТИРОВАТЬ:
Немного покопавшись, я нашел вариант переподключения сигнала:
http://qt-project.org/forums/viewthread/6820
В основном, вы переопределены QObject::connect
и затем вы отслеживаете соединения и обрабатываете их так, как вам нужно. Таким образом, в этом случае вы должны сохранить список всех подключений сигнала newConnection
и сохраните его в списке, чтобы при его отключении вы могли подключить его снова. Обязательно позвони QObject::connect
в конце повторной реализации.
Другим вариантом при прохождении этого маршрута будет пойти и просто перенаправить туда соединения. Когда соединение запрашивается у newConnection
, переместите его туда, чтобы myNewConnection
,
Надеюсь, это поможет.
Грязным хаком было бы очень кратко заблокировать сигналы от 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()
, Как я уже сказал, это грязный хак.