Websockets Erfahrung in Entwicklung und Betrieb. Wir modifizieren den Kunden

Ich begrüße alle, die sich für dieses Protokoll interessieren, und entschuldige mich im Voraus für meine übermäßig emotionale Bemerkung. Befasst sich mit diesem Thema in Eile (wenn nötig), aber für eine lange Zeit. In dieser Hinsicht hat sich eine gewisse Praxis zum Entwerfen und Verwenden dieser Technologie angesammelt und gebildet. Die Service-Implementierungsoption wird hier beschrieben. Seitdem ist viel Wasser geflossen. Die Grundprinzipien sind dieselben geblieben, aber der Code der Anwendung selbst wurde auf natürliche Weise geändert. An einigen Stellen wurden unkritische Fehler gefunden und behoben, irgendwo wurde die Steuerung von Programmabläufen, Listen offener Verbindungen usw. optimiert.

Wie Sie wissen, gibt es neben der Serverseite auch eine Clientseite. Und hier möchte ich genauer aufhören und beschreiben, was ich zu bewältigen hatte und was beeinflusst werden konnte. Wenn Sie JavaScript verwenden, können Sie natürlich nicht besonders "herumtollen", da alles bereit und geschlossen ist, aber Sie können etwas über den Client in Java erzählen.

Egal wie gut Sie als Programmierer sind, es ist immer schwierig, etwas Einzigartiges zu entwickeln und dann Ihre eigenen Verse zu debuggen. Aus diesem Grund bin ich einmal der Versuchung erlegen, etwas bereits Fertiges zu finden, sodass Sie es sofort in meinen Projekten verwenden können. Die Auswahlkriterien für das fertige Modul waren einfach. Ich wollte einen vollständigen, funktionierenden Code in Java mit minimalem Overhead erhalten.

Als Ausgewählter habe ich mich für ein Modul entschieden, das Apache-Bibliotheken verwendet. Insbesondere diese:

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

Was kann über ihre Verwendung gesagt werden? Die Apache-Software war schon immer für ihre Zuverlässigkeit, Raffinesse und Optimalität bekannt. Der Client für Android hat erfolgreich funktioniert. Die fertige * .apk-Datei war nicht kritisch groß. Es gab keine besonderen Beschwerden über die Arbeit dieser Bibliotheken. Aber das Leben ist immer schlauer als wir. Und die Zeit (und dieser Zeitraum beträgt ungefähr vier bis fünf Jahre) nimmt seine eigenen Anpassungen vor. Die Anwendung wurde geschrieben, als es eine Version von Android 4.2 - 4.4 gab. Und der Bedarf an neuen Lösungen entstand bereits in diesem Jahr, als Geräte mit der Version 10 bereits in vollem Gange waren.

Die Entwicklung wurde zu der Zeit auf Eclipse für Windows 7 durchgeführt. Das Aktualisieren des Android SDK auf die gewünschte Stufe führte dazu, dass die kleine 128-GB-SSD-Festplatte voll war. Ich musste zu Android Studio wechseln. Außerdem musste ich das Basisbetriebssystem ändern. Ich habe versucht, Ubuntu zu installieren (ich kann mich nicht an die Versionsnummer erinnern) und benutze Studio bereits in dieser Umgebung. Aber auch hier, Misserfolg, wollte Andriod Studio hartnäckig nicht installieren.

Warum - schon vergessen. Am Ende installierte er auf Anraten von Freunden die neueste Version von Linux-Mint, und siehe da, das Toolkit legte es ohne Beschwerden auf. Dann geschah genau das, worauf alle diese Details zurückzuführen waren, nämlich die Routine, Tests zu schreiben.

Also, was wurde in diesem Tyagomotin erwartet? Beginnen wir mit der Tatsache, dass Apache von der offiziellen Website aktuellere Versionen der oben genannten Bibliotheken kopiert hat. Fügte sie dem Projekt hinzu und ... Und Kompilierungsfehler fielen ein. Die Zeit ist vergangen, Klassenschnittstellen haben sich geändert. Also musste ich (aus Zeitgründen, um neue Bibliotheken zu studieren) zu den alten Versionen zurückkehren. Aber ...

Andererseits suchen wir nicht nach einfachen Wegen. Ich dachte, warum brauche ich diese Bibliotheken komplett? Die Texte dieser Pakete sind. Was ist, wenn Sie nur die erforderlichen Klassen nehmen und daraus ziehen? Wenn Sie die Texte des Moduls für die Arbeit mit Web-Sockets betrachten, sehen Sie außerdem nur zwei Klassen aus diesen Bibliotheken.

Also habe ich ein neues Projekt erstellt und angefangen, die notwendigen Klassen hinzuzufügen. Am Ende stellte sich heraus, dass für eine erfolgreiche Kompilierung 32 Klassen herausgezogen werden müssen. Und doch hat das Projekt funktioniert. Alles atmete. Die Verbindung zum Web-Socket-Dienst war erfolgreich. Und alles wäre in Ordnung, bemerkte aber das folgende für mich unverständliche Ereignis. Beim Schließen der Verbindung hat das für die Verbindung verantwortliche Modul eine Ausnahme ausgelöst:

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

Ich war ratlos. Folgendes verwirrt. Die Verbindung zum Server war erfolgreich. Pakete kamen und gingen erfolgreich. Aber warum genau hat die Schließung eine Ausnahme ausgelöst? Außerdem war auf dem Server alles Standard. Offensichtlich hatten die Kunden irgendwo im Text eine Kleinigkeit, die sich auf die Schließung auswirkte. Darüber hinaus zeigten die Texte ein solches Merkmal. Gemäß Abschnitt 7.1.1 des Dokuments besteht das Schließen auf der Client-Seite nicht nur aus dem Aufrufen der close () -Methode, sondern auch aus dem Bilden und Senden eines Pakets mit Operationscode 8 (Abschlussoperation). In diesem Fall würde der Server sein Abschlusspaket senden, wonach der Client die Verbindung schließen würde. In unserem Fall wurde eine solche Abfolge von Anrufen jedoch nicht beobachtet. Es hat nur die Schließfunktion aufgerufen und das wars. Im Allgemeinen gab es etwas zu überlegen. Und je mehr ich die Texte dieses Moduls mit dem Paket-Parser durchgesehen und studiert habe, desto weniger hat es mir gefallen, desto mehr bestand der Wunsch, sie mit meiner Vision des Protokolls neu zu schreiben. Am Ende wurde beschlossen, diese „Arbeitskraft“ auszuführen.

Was passte eigentlich nicht, was löste in diesen Modulen „Bürgerprotest“ aus? Erstens die Organisation der Interaktion zwischen dem Modul der direkten Verbindung mit dem Server und dem Paket-Parser. Es stellte sich heraus, dass das Verbindungsmodul mit dem Server interagierte und einen Parser generierte, an den es einen Link als Parameter an sich selbst übergab. Infolgedessen wurde dem Parser die Befugnis übertragen, Entscheidungen über bevorstehende Netzwerkereignisse zu treffen. In diesem Zusammenhang stellte sich die Frage, aber ist es gut? Wäre es nicht besser, wenn das Parser-Modul seine entsprechende Mission erfüllen und das Ergebnis seiner Arbeit zurückgeben würde, aber die Steuerungsentscheidung über Ereignisse von dem Objekt ausgeführt würde, das den Parser generiert hat? In diesem Fall würde eine strenge Hierarchie der Interaktion zwischen Objekten festgelegt. (Hier können Sie natürlich diskutieren, was besser ist - eine Hierarchie oder ein Netzwerk, aber dann entfernen wir uns vom Thema.)

Das zweite, was mich dazu brachte, alles neu zu schreiben, war die Struktur des Parsers. Dieses Objekt (Klasse) sollte zwei Hauptfunktionen erfüllen, nämlich das Bilden eines Datenpakets zur Übertragung an den Server und das Parsen von vom Server empfangenen Paketen. Es waren also diese beiden Funktionen, die im Großen und Ganzen nicht passten. Und damit.

Stellen Sie sich vor, ein Netzwerkereignis ist aufgetreten, ein Paket ist angekommen. Was hat HybiParser in diesem Fall getan? Dieses Objekt liest die ersten beiden Bytes byteweise aus dem Eingabestream des Sockets und bestimmt die nächsten Aktionen: Analysieren der Datengröße, Maske usw. Infolgedessen wurde dies in mehreren Leseoperationen aus dem Eingabestream des Sockets implementiert. Darüber hinaus wurde das Parsen durch Lesestufen erschwert, was den Algorithmus weiter erschwerte. Und wieder stellte sich die Frage, ist es richtig, warum solche Schwierigkeiten? Ist es nicht besser, ein Paket als eine Operation zu betrachten, zumal die Größe der eingehenden Daten bestimmt werden kann?

Der dritte. Ein kontroverser Aspekt der Arbeit des Parsers scheint der „ewige“ Paketannahmezyklus zu sein. Der Zyklus läuft in einem separaten Programmstrom ab. Irgendwann schließt die Steckdose. Was kommt als nächstes, die übliche Ausnahmebehandlung? Oder was machen? Nein, ich bin nicht gegen den Mechanismus der Ausnahmen, aber es wäre einfach schön, diesen Umstand und die Reaktion darauf im Voraus vorherzusehen. Beispielsweise konnte ein Synchronisationsmechanismus als Lösung vorgeschlagen werden, bei dem die regelmäßige Beendigung des Zyklus und dementsprechend der Programmstrom stattfinden wird.

Als Ergebnis der Bewertung all dieser Nuancen wurden die folgenden Anforderungen für den Entwurf der erforderlichen Module ermittelt:

  • Module müssen unabhängig von Bibliotheken von Drittanbietern sein.
  • Module sollten einfach und leicht in andere Projekte zu integrieren sein.
  • Module sollten für zukünftige Funktionserweiterungen bereit sein.

Nun, um nicht für übermäßige Kritik an der vorherigen vorgefertigten Lösung verantwortlich zu sein, fügen wir hinzu, dass ein Teil der vorgefertigten und nicht kritisierenden Funktionen sicher auf die neue Implementierung übertragen werden konnte. Nun, das ist alles und wie sie auf dem XXII. Kongress der KPdSU sagten: „Unsere Ziele sind klar, die Aufgaben sind definiert. Zu arbeiten, Kameraden! Für die neuen Siege des Kommunismus! “

Im Allgemeinen beschreibe ich kurz meinen Vorschlag und konzentriere mich nur auf die wichtigsten Punkte, um Sie nicht zu überladen, lieber Leser, um Ihre wertvolle Zeit nicht zu verlieren.
Die vorgeschlagene Implementierung enthält also nur vier Module (gemäß den obigen Anforderungen der Apache-Bibliothek oder einzelne Klassen davon wurden nicht in das Projekt aufgenommen):

  • Modul für globale Konstanten des WebSocket-Protokolls 07;
  • Hilfsausnahmeklasse;
  • Web-Socket-Client;
  • Modul zum Parsen von Paketen der WebSocket-Protokollstufe 07.

In den ersten beiden Modulen ist die Implementierung trivial, es gibt nichts zu beachten. Das Client-Modul implementiert die Kontrolle über die Verbindung zum Server, und ich möchte auf die folgenden Punkte eingehen. Die Funktion zum Öffnen der Verbindung enthält eine Reihe von Headern, die vom Server stammen. Hier wird das Parsing des Sec-WebSocket-Accept-Schlüssels tatsächlich implementiert, und in unserem Fall wird das Parsing ohne Verwendung der Apache-Bibliotheken durchgeführt.

Achten Sie als nächstes auf die Funktionen der Paketschleifensteuerung. Die Implementierung ist durch ein Synchronisationsobjekt trivial.

Der nächste zu beachtende Punkt ist die Funktion der Schleife. Der Zyklus ist nicht "ewig", sondern mit dem Exit unter der Bedingung der Überprüfung des Synchronisationsobjekts. In einem Zyklus wird ein ankommendes Paket in einer Operation gelesen. Das Paket wird vom entsprechenden Objekt zum Parsen analysiert. Als nächstes wird eine Managemententscheidung für das bevorstehende Netzwerkereignis getroffen.

Um die Beschreibung zu vervollständigen, wird auf die Funktion zum sicheren Trennen der Verbindung hingewiesen. Dies geschieht wie folgt. Ein Verbindungsbeendigungspaket wird an den Server gesendet und eine Variable der Runnable-Klasse wird generiert, die dann zur Ausführung durch die postDelayed-Funktion im Warteschlangenprozessor abgelegt wird. Einer der Parameter ist die Betriebsverzögerung in Millisekunden. In der Variablen Runnable enthält die Ausführungsfunktion eine Folge von Aufrufen, wenn der Programmstrom beendet und die Verbindung zum Server getrennt wird. Für den Fall, dass das schließende Antwortpaket früher eintrifft, wird diese Position in der Verarbeitungsliste gelöscht.

Die Klasse, die die Analyse von WebSocket-Paketen implementiert, enthält zwei Methoden, die beachtet werden müssen: das Parsen selbst und die Bildung eines Pakets für die Übertragung gemäß den relevanten Parametern. Beim Parsen werden alle Flags und Daten des empfangenen Pakets in öffentlichen Klassenvariablen gespeichert. Warum öffentlich? Ja, der Einfachheit halber, um keine zusätzlichen get / set-Funktionen für sie zu erstellen.

Nun, eigentlich, lieber Leser. Das Archiv mit dem Projekt für Android Studio ist beigefügt . Ich werde keinen Anspruch auf die Verwendung dieser Texte in Ihren Projekten erheben. Konstruktive Kritik wird akzeptiert. Beantworten Sie Fragen so weit wie möglich.

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


All Articles