Bonjour,
Je suis en train de construire une architecture distribuée constituée de trois process, A, B et C.
B et C communiquent à l’aide de sockets, B étant client et C serveur.
A et C sont lancés de manière autonome, et sont donc commandés à l’aide d’une interface (GUI)
B est créé et lancé par A. A et B communiquent par pipe.
Autrement dit, C est une ressource distante. A est un processus maître qui lance localement des processus de type B qui ont besoin d’utiliser la ressource distante C.
Pour construire cette architecture, je commence par construire la relation entre B et C, et par tester cette relation, en lançant B de manière autonome (avec une interface).
Je construis la communication par sockets entre B et C, en partant de l’exemple « client-server » intégré à wxWidgets (wxWidgets\samples\sockets\).
Dans cet exemple,
Côté server :
- Dans le constructeur de MyFrame :
o Un nouvel objet, m_server, de classe wxSocketServer, est créé, et associé à l’adresse 3000 (port).
o Un EventHandler est associé à m_server : (MyFrame, SERVER_ID)
o Le filtre d’évènements est limité à wxSOCKET_CONNECTION_FLAG
- Dans la table d’évènements de MyFrame, SERVER_ID est associé à la méthode « MyFrame ::OnServerEvent(…event) »
- MyFrame ::OnServerEvent(…event)
o Cette méthode ne peut donc à priori être lancée que par un évènement de type « CONNECTION ». Elle est donc exécutée lorsque le client envoie une commande « Connect », vers la socket server.
o Cette méthode lance l’exécution de la fonction Accept() de m_server, et crée une wxSocketBase, sock, associée à la relation entre le server et le client qui vient de se connecter.
o Ensuite, un EventHandler est associé à sock, (MyFrame, SOCKET_ID).
o Le filtre d’évènements est alors établi à « INPUT » | « LOST »
Côté client :
- Dans le constructeur de MyFrame :
o Un nouvel objet, m_sock, de classe wxSocketClient() est créé.
o Un EventHandler lui est associé : (MyFrame, SOCKET_ID)
o Le filtre d’évènements est établi à : « CONNECTION »|« INPUT »|« LOST »
- Dans la table d’évènements de MyFrame :
o SOCKET_ID est associé à MyFrame ::OnSocketEvent
o Un évènement « Menu », CLIENT_OPEN, est associé à MyFrame ::OnOpenConnection()
- MyFrame ::OnOpenConnection(…event) :
o Dans le mode normal, proposé par l’exemple, le processus client est commandé par une interface. C’est donc la commande « OpenSession » de cet interface, qui entraîne l’exécution de OnOpenConnection(…event).
o Exécute la fonction Connect sur m_sock, avec comme paramètres :
Une adresse composé de « adresse IP4 du server » + 3000 (port)
« false » : ce qui signifie que l’appel Connect est en mode « non bloquant ». Autrement dit, la fonction Connect n’empêche pas l’exécution du thread principal du client, qui se poursuit sans attendre la réponse du server
o La fonction Connect étant lancée en monde non-bloquant, en général, le thread principal poursuit son exécution, sans que la connexion soit valide (du moins du côté client). C’est sans doute pourquoi le concepteur du programme a fait suivre la fonction Connect de la fonction WaitOnConnect(time_out).
o La fonction WaitOnConnect renvoie :
« true », soit si la connexion avec le server est établie correctement, soit si le server est inaccessible.
« false » en cas de dépassement du time_out.
o Si le time_out est bien réglé, dans le cas général, le server étant présent, la fonction WaitOnConnect renvoie true, et la connexion est établie correctement.
Mes problèmes commencent lorsque je veux exécuter exactement la même suite de fonctions, mais à partir de A, B n’étant plus lancée en mode autonome, mais étant créé par A. Pour clarifier le débat, donnons deux noms différents à ces deux modes d’exécution de la liaison B-C :
- mode « autonome » dans le cas où B est lancé de manière autonome, et dispose de son propre interface.
- Mode « commandé » dans le cas où B est créé et lancé par A.
Si je laisse le paramètre de blocage de Connect à false, le thread principal se poursuit, dans le cas commandé, exactement comme dans le cas autonome, c’est-à-dire que l’on ressort de la fonction Connect, dans un état « non-connecté ».
La différence est maintenant que, en mode commandé, le process B reste bloqué dans la fonction WaitOnConnect, tandis qu’en mode autonome, il ne reste pas bloqué dans cette fonction.
J’ai réussi à résoudre ce premier problème, dans le mode commandé, en mettant le paramètre de blocage de Connect, à « true ». En mode commandé, la fonction Connect ressort alors avec une connexion valide, et je n’ai plus besoin de la fonction WaitOnConnect, que je supprime donc.
Je suis alors tombé sur un deuxième problème : la première instruction Write, à l’aide de laquelle le process client était censé envoyer un message au server, est restée dans un état bloqué.
J’ai réussi à résoudre ce problème, à l’aide de l’instruction :
m_sock->SetFlags(wxSOCKET_NOWAIT)
placée avant Write.
J’ai donc maintenant une architecture générale qui fonctionne, mais je reste insatisfait, parce que je ne comprends pas bien, en profondeur, comment elle fonctionne. J’aimerais savoir si quelqu’un pourrait m’expliquer les raisons de la suite de fonctionnements et dysfonctionnements que j’ai constatés. Je suspecte une sorte d’interférence entre les sockets et le système de gestion des évènements. J’imagine que dans le mode autonome, le thread principal, dès qu’il a fini d’exécuter les tâches demandées par le programmeur, retourne dans une sorte de boucle dans laquelle il attend les évènements. Tandis qu’en mode commandé, ce n’est peut-être pas le cas. Cela signifierait que la fonction WaitOnConnect, à la différence de la fonction Connect, se branche sur la boucle de lecture des évènements. Si cette boucle pour une raison ou pour une autre, ne fonctionne pas, la fonction WaitOnConnect(), dans le mode commandé, bouclerait indéfiniment. Ceci qui expliquerait la différence de comportement entre les deux modes…
Zorglbug
Partager