Websockets Expérience en développement et exploitation. Nous modifions le client

Je souhaite la bienvenue à tous ceux qui s'intéressent à ce protocole, et je m'excuse à l'avance pour ma note trop émotionnelle. Engagé dans ce sujet à la hâte (si nécessaire), mais depuis longtemps. À cet égard, une certaine pratique de conception et d'utilisation de cette technologie s'est accumulée et s'est formée. L'option d'implémentation du service est décrite ici . Depuis lors, beaucoup d'eau a coulé. Les principes de base sont restés les mêmes, mais le code de l'application lui-même a subi des modifications naturelles. À certains endroits, des erreurs non critiques ont été trouvées et corrigées, quelque part le contrôle des flux de programmes, des listes de connexions ouvertes, etc. a été optimisé.

Mais en plus du côté serveur, comme vous le savez, il y a un côté client. Et ici, je voudrais m'arrêter plus en détail et décrire ce que j'ai dû affronter et ce qui pourrait être influencé. Bien sûr, lorsque vous utilisez JavaScript, vous ne pourrez pas particulièrement "gambader", car tout est prêt et fermé, mais vous pouvez dire quelque chose sur le client en Java.

Peu importe à quel point vous êtes bon programmeur, il est toujours difficile de développer, d'inventer quelque chose d'unique, puis de déboguer vos propres versets. Par conséquent, à un moment donné, j'ai succombé à la tentation de trouver quelque chose de déjà prêt, vous permettant de l'utiliser immédiatement dans mes projets. Les critères de sélection pour le module fini étaient simples. Je voulais obtenir un code complet et fonctionnel en Java avec un minimum de frais généraux.

En tant que sélectionné, je me suis installé sur un module utilisant les bibliothèques Apache. En particulier, ceux-ci:

  • apache-mime4j-core-0.7.2.jar;
  • httpclient-4.2.1.jar;
  • httpcore-4.2.1.jar;
  • httpmime-4.2.1.jar.

Que dire de leur utilisation? Le logiciel Apache a toujours été célèbre pour sa fiabilité, sa sophistication et son optimalité. Le client pour Android a fonctionné avec succès. La taille du fichier * .apk terminé n'était pas critique. Il n'y a eu aucune plainte particulière concernant le travail de ces bibliothèques. Mais la vie est toujours plus intelligente que nous. Et le temps (et cette période est d'environ quatre à cinq ans) fait ses propres ajustements. L'application a été écrite lorsqu'il existait une version d'Android 4.2 - 4.4. Et le besoin de nouvelles solutions est déjà apparu cette année, lorsque les appareils avec la version 10 battaient déjà leur plein.

Le développement a été effectué à l'époque sur Eclipse pour Windows 7. La mise à jour du SDK Android au niveau souhaité a conduit au fait que le petit disque dur SSD de 128 Go était plein. J'ai dû passer à Android Studio. De plus, j'ai dû changer le système d'exploitation de base. J'ai essayé d'installer Ubuntu (je ne me souviens pas du numéro de version) et d'utiliser Studio déjà dans cet environnement. Mais encore une fois, échec, Andriod Studio ne voulait obstinément pas installer.

Pourquoi - déjà oublié. En fin de compte, sur les conseils d'amis, il a installé la dernière version de Linux-Mint, et, voilà, la boîte à outils s'y est posée sans aucune plainte. Ensuite, ce qui s'est réellement passé, précisément à cause de quoi tous ces détails ont été décrits, à savoir la routine des tests d'écriture.

Alors, qu'attendait-on de cette tyagomotine? Commençons par le fait que depuis le site officiel Apache a copié des versions plus récentes des bibliothèques ci-dessus. Les a ajoutés au projet et ... Et des erreurs de compilation sont tombées. Le temps a passé, les interfaces de classe ont changé. J'ai donc dû (faute de temps pour étudier de nouvelles bibliothèques) revenir aux anciennes versions. Mais ...

Mais là encore, nous ne cherchons pas de moyens faciles. J'ai pensé, pourquoi ai-je complètement besoin de ces bibliothèques? Les textes de ces packages sont. Que faire si vous ne prenez et n'en retirez que les cours nécessaires? De plus, en considérant les textes du module pour travailler avec les sockets web, vous ne pouviez voir que deux classes de ces bibliothèques.

J'ai donc créé un nouveau projet et commencé à ajouter les classes nécessaires. Et à la fin, il s'est avéré que pour une compilation réussie, il était nécessaire de retirer 32 classes. Et pourtant, oui, le projet a fonctionné. Tout respirait. La connexion au service de socket Web a réussi. Et tout irait bien, mais a remarqué l'événement suivant, incompréhensible pour moi. Lors de la fermeture de la connexion, le module responsable de la connexion a levé une exception:

java.io.EOFException
à java.io.DataInputStream.readByte (DataInputStream.java:77)
sur com.example.wsci.HybiParser.start (HybiParser.java:112)
sur com.example.wsci.WebSocketClient $ 1.run (WebSocketClient.java:144)
à java.lang.Thread.run (Thread.java:818)

J'étais perdu. Le suivant confus. La connexion au serveur a réussi. Les colis sont venus et sont partis avec succès. Mais, pourquoi exactement la fermeture lançait-elle une exception? De plus, tout était standard sur le serveur. De toute évidence, quelque part dans le texte, les clients ont eu une bagatelle qui a affecté la fermeture. De plus, les textes ont montré une telle caractéristique. Selon la clause 7.1.1 du document, la fermeture côté client consiste non seulement à appeler la méthode close (), mais à former et envoyer un paquet avec le code d'opération 8 (opération de fermeture). Dans ce cas, le serveur enverrait son paquet de fermeture, après quoi le client fermerait la connexion. Mais dans notre cas, une telle séquence d'appels n'a pas été observée. Il a juste appelé la fonction close et c'est tout. En général, il y avait quelque chose à penser. Et plus je regardais et étudiais les textes de ce module avec l'analyseur de paquets, moins j'aimais ça, plus j'avais envie de les réécrire avec ma vision de ce protocole. Finalement, il a été décidé de réaliser cet «exploit de travail».

Qu'est-ce qui ne convenait pas réellement, qu'est-ce qui a provoqué une «protestation civile» dans ces modules? Tout d'abord, l'organisation de l'interaction entre le module de connexion directe avec le serveur et l'analyseur de paquets. Il s'est avéré que le module de connexion interagissait avec le serveur, générait un analyseur auquel il se transmettait un lien en tant que paramètre. En conséquence, l'analyseur s'est vu déléguer le pouvoir de prendre des décisions sur les événements du réseau à venir. À cet égard, la question s'est posée, mais est-elle bonne? Ne serait-il pas préférable que le module analyseur remplisse sa mission correspondante, renvoie le résultat de son travail, mais la décision de contrôle sur les événements serait exécutée par l'objet qui a généré l'analyseur? Dans ce cas, une hiérarchie stricte d'interaction entre les objets serait déterminée. (Ici, bien sûr, vous pouvez débattre de ce qui est mieux - une hiérarchie ou un réseau, mais ensuite nous nous éloignons du sujet.)

La deuxième chose qui m'a donné envie de tout réécrire était la structure de l'analyseur. Cet objet (classe) devrait remplir deux fonctions principales, à savoir former un paquet de données pour la transmission au serveur et l'analyse des paquets reçus du serveur. Ce sont donc ces deux fonctions qui ne convenaient pas, dans l'ensemble. Et voici le truc.

Imaginez qu'un événement de réseau se soit produit, un paquet est arrivé. Qu'a fait HybiParser dans ce cas? Cet objet a lu les deux premiers octets du flux d'entrée du socket par octet et a déterminé ses actions suivantes: analyse de la taille des données, masque, etc. En conséquence, cela a été implémenté dans plusieurs opérations de lecture à partir du flux d'entrée du socket. De plus, l'analyse était compliquée par les étapes de lecture, ce qui compliquait encore l'algorithme. Et encore une fois la question se pose, est-il vrai, pourquoi de telles difficultés? N'est-il pas préférable de considérer un paquet comme une opération, d'autant plus que la taille des données entrantes peut être déterminée?

Le troisième. Il semble qu'un autre aspect controversé du travail de l'analyseur soit le cycle d'acceptation des paquets «éternel». Le cycle s'exécute dans un flux de programme distinct. À un moment donné, le socket se ferme. Quelle est la prochaine étape, la gestion habituelle des exceptions? Ou quoi faire? Non, je ne suis pas contre le mécanisme des exceptions, mais il serait juste bien de prévoir à l'avance cette circonstance et sa réaction. Par exemple, il a été possible de proposer un mécanisme de synchronisation comme solution, au cours duquel l'achèvement régulier du cycle et, par conséquent, le flux de programme se produiront.

À la suite de l'évaluation de toutes ces nuances, les exigences suivantes pour la conception des modules nécessaires ont été déterminées:

  • Les modules doivent être indépendants des bibliothèques tierces;
  • Les modules doivent être simples et faciles à intégrer dans d'autres projets;
  • Les modules doivent être prêts pour une future extension fonctionnelle.

Eh bien, afin de ne pas être blâmé pour les critiques excessives de la solution prête à l'emploi précédente, nous ajoutons à cela, en outre, qu'une partie des fonctions toutes faites et non critiques pourrait être transférée en toute sécurité à la nouvelle mise en œuvre. Eh bien, c’est tout, et comme ils l’ont dit au XXIIe Congrès du PCUS: «Nos objectifs sont clairs, les tâches sont définies. Pour travailler, camarades! Pour les nouvelles victoires du communisme! ”

En général, afin de ne pas vous surcharger, cher lecteur, de ne pas vous priver de votre temps précieux, je décrirai brièvement ma proposition et me concentrerai uniquement sur les points clés.
Ainsi, l'implémentation proposée ne contient que quatre modules (conformément aux exigences ci-dessus de la bibliothèque Apache ou des classes individuelles d'entre elles n'étaient pas incluses dans le projet):

  • Module de constantes globales du protocole WebSocket 07;
  • Classe d'exception d'assistance;
  • Client de socket Web;
  • Module d'analyse des paquets du protocole WebSocket niveau 07.

Dans les deux premiers modules, l'implémentation est triviale, il n'y a rien à considérer. Le module client implémente le contrôle de la connexion au serveur, et je voudrais m'attarder sur les points suivants. La fonction de connexion ouverte possède une boucle d'en-têtes provenant du serveur. Ici, l'analyse de clé Sec-WebSocket-Accept est réellement implémentée, et dans notre cas, l'analyse est effectuée sans utiliser les bibliothèques Apache.

Ensuite, faites attention aux fonctions de contrôle de boucle de paquets. L'implémentation est triviale, à travers un objet de synchronisation.

Le prochain point à respecter est la fonction de la boucle. Le cycle n'est pas "éternel", mais avec la sortie à condition de vérifier l'objet de synchronisation. Dans un cycle, un paquet arrivant est lu en une seule opération. Le package est analysé par l'objet correspondant pour l'analyse. Ensuite, une décision de gestion est prise sur le prochain événement du réseau.

Pour compléter la description, nous notons la fonction de terminer la connexion en toute sécurité. Cela se fait comme suit. Un paquet de terminaison de connexion est envoyé au serveur et une variable de la classe Runnable est générée, qui est ensuite placée dans le processeur de file d'attente pour exécution via la fonction postDelayed, dont l'un des paramètres est le retard de fonctionnement en millisecondes. Dans la variable Runnable, la fonction d'exécution contient une séquence d'appels lorsque le flux de programme se termine et que la connexion au serveur est fermée. Dans le cas où le paquet de réponse de fermeture arrive plus tôt, cette position dans la liste de traitement est supprimée.

La classe qui implémente l'analyse des paquets WebSocket contient deux méthodes qui nécessitent de l'attention: l'analyse elle-même et la formation d'un paquet pour la transmission en fonction des paramètres pertinents. Lors de l'analyse, tous les indicateurs et données du paquet reçu sont stockés dans des variables de classe publiques. Pourquoi public? Oui, pour plus de simplicité, afin de ne pas leur créer de fonctions get / set supplémentaires.

Eh bien, en fait, cher lecteur. L'archive avec le projet pour Android Studio est jointe . Je ne revendiquerai pas l'utilisation de ces textes dans vos projets. La critique constructive est acceptée. Répondez aux questions autant que possible.

Source: https://habr.com/ru/post/fr478546/


All Articles