2013-01-03 2 views
1

Je veux créer un serveur SSL, donc je sous-classe QTcpServer et je remplace incomingConnection(), où je crée un QSslSocket, définissez son descripteur, et appelez QSslSocket::startServerEncryption. À ce stade, je dois attendre que le signal QSslSocket::encrypted() soit émis, et ce n'est qu'après que mon serveur devrait émettre le signal newConnection(). Le code client penserait alors qu'il utilise un QTcpSocket, mais utilisera en fait une socket sécurisée.Lors du sous-classement de QTcpServer, comment puis-je retarder l'émission du signal newConnection()?

Mais QTcpServer émet toujoursnewConnection() après avoir appelé incomingConnection() (j'ai regardé dans the source of QTcpServer):

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

Donc ma question est, est-il un moyen que je peux empêcher QTcpServer d'émettre newConnection(), jusqu'à ce que je suis prêt à l'émettre moi-même?

La raison pour laquelle je veux que c'est que je veux que ma classe pour pouvoir être utilisé comme solution de remplacement de QTcpServer, par un code qui ne sait pas qu'il utilise, donc il doit se comporter exactement comme QTcpServer:

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

Mon code pour la classe sslserver est actuellement ceci:

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

Répondre

2

Voici une idée qui pourrait fonctionner dans ce cas: redéfinir le signal newConnection dans votre sous-classe QTcpServer. Si vous faites cela, les objets connectés à une instance de votre serveur ne recevront pas la "version" du signal de QTcpServer, seulement celle que vous émettez directement de votre sous-classe.

est ici une preuve de concept: classe A est le QTcpServer, foo est le signal que vous essayez de « pirater », bar est juste une autre (hypothétique) des signaux de QTcpServer vous n'avez pas besoin de toucher.

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

La classe B est votre sous-classe. Notez qu'il redéfinit le signal foo, mais ne fait rien à 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); 
}; 

classe C est un client potentiel, relie les signaux/slots d'une instance B exactement comme il le ferait pour une instance 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; 
     } 
}; 

est ici la sortie de la construction d'un C:

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

... et rien d'autre. Aemit foo(1) n'est pas connecté à C fente foo, il n'arrivera jamais à C. Aemit bar(1) travaillé comme prévu, ce signal est intacte.

Avec cette configuration, vous pouvez émettre newConnection lorsque votre classe est prête, la version QTcpServer du signal ne sera pas reçue par les objets de votre utilisateur.

1

pour être une baisse réelle en remplacement, vous devrez probablement modifier la source réelle de Qt, parce que vous normalement ne pouvez pas réimplémentez Private appels de classe.

Si vous êtes le seul à utiliser le remplacement d'un, et vous contrôlez les classes qui se connectent au signal newConnection ...

Il suffit de connecter newConnection à votre propre emplacement handleNewConnection. Lorsque la connexion sécurisée est prête, émettez myNewConnection et connectez-la aux éléments qui auraient été connectés à newConnection.

EDIT: Après un peu de creuser, je l'ai trouvé une option de se reconnecter un signal:

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

Fondamentalement, vous réimplémentez QObject::connect, et vous garder une trace des connexions et de les traiter de la comme vous devez le faire. Donc, dans ce cas, vous conservez une liste de toutes les connexions du signal newConnection et conservez-le dans une liste. Lorsque vous le déconnectez, vous pouvez le reconnecter. Assurez-vous d'appeler le QObject::connect à la fin de la réimplémentation.

Une autre option lors de cette route serait d'aller et de réacheminer les connexions là-bas. Lorsqu'une connexion est demandée depuis newConnection, déplacez-la vers myNewConnection.

Espérons que ça aide.

+1

Qu'en est-il de déconnecter le signal et de le reconnecter plus tard? C'est possible? Et non, certainement pas de réécriture des sources Qt :) – sashoalm

+0

Vous pouvez l'essayer: ['QObject :: disconnect'] (http://qt-project.org/doc/qt-4.8/qobject.html#disconnect). Je ne l'ai pas fait assez pour le savoir bien. – phyatt

+1

Le problème est que j'ai besoin de rebrancher le signal plus tard, donc j'ai besoin d'obtenir la liste des récepteurs pour ce signal en quelque sorte. Je publie ceci comme une nouvelle question - http://stackoverflow.com/q/14144415/492336. – sashoalm

1

Un hack sale serait de bloquer très brièvement les signaux de QTcpServer. Puisque vous savez que newConnection() sera émis juste après votre retour de SslServer::incomingConnection(), appelez le this->blockSignals(true); juste avant votre retour. Cela empêchera d'invoquer tous les emplacements auxquels il est connecté. Pour vous assurer de recevoir les signaux suivants, débloquez les signaux dès que possible. Je suppose que le plus tôt possible serait correct lorsque le contrôle retourne à la boucle d'événements, donc un QTimer :: singleShot pourrait le faire.

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

L'inconvénient de c'est que vous perdrez tous les signaux qui pourraient légitimement être emited entre incomingConnection() et unblockSignals(). Comme je l'ai dit, c'est un hack sale.