Masken und Steuerframes
Nachrichten in Stücken
Jeder encode_frame(OP_TEXT, "Hel", fin=False), ein Frame, der nicht final ist. Dieser Frame ist die Hälfte eines echten Beispiels. RFC 6455, Abschnitt 5.7, sendet “Hello” als zwei Frames:
01 03 48 65 6c
80 02 6c 6f
Der erste Frame ist 01: FIN nicht gesetzt, 80: FIN gesetzt, Opcode 0, dann die Länge 2 und “lo”. Zusammen tragen sie eine Textnachricht, “Hello”. Das ist Fragmentierung, und diese Lektion liest sie, ohne Code dafür zu schreiben.
Die Regeln aus Abschnitt 5.4
Eine Nachricht, die auf mehrere Frames verteilt ist, ihre Fragmente, folgt drei Regeln:
Der erste Frame trägt den Opcode der Nachricht, 1 für Text oder 2 für binär, mit nicht gesetztem FIN.
Alle mittleren Frames tragen Opcode 0, Fortsetzung, mit nicht gesetztem FIN.
Der letzte Frame trägt Opcode 0 mit gesetztem FIN.
Opcode 0 sagt “dieser Frame setzt die Nachricht fort, die gerade läuft”; nur der erste Frame sagt, welche Art von Nachricht es ist. FIN markiert das Ende, also sammelt der Empfänger Payloads, bis ein Fortsetzungsframe mit gesetztem FIN ankommt, und fügt sie dann zusammen. Eine Nachricht in einem Frame ist einfach der Fall, in dem der erste Frame auch der letzte ist: Opcode 1 oder 2 mit gesetztem FIN, die einzige Art, die dein Codec bisher gesehen hat.
Dieser Block liest beide Frames mit den Bitoperationen aus Kapitel 3 und fügt ihre Payloads zusammen:
Welcher Frame beendet eine fragmentierte Nachricht?
Warum eine Nachricht aufteilen
Zwei Situationen bringen einen Sender dazu, eine Nachricht aufzuteilen. Die erste ist ein Sender, der zu Beginn nicht weiß, wie lang die Nachricht wird, zum Beispiel einer, der Daten sendet, während er sie erzeugt. Der Header eines Frames nennt die Payload-Länge vor der Payload, also sendet ein solcher Sender, was er hat, als Fragment und markiert das letzte Stück mit FIN, wenn er fertig ist.
Die zweite ist eine lange Nachricht, die nicht alles andere blockieren soll. Lektion 3 hat Steuerframes eine dritte Regel gegeben: Sie dürfen zwischen den Fragmenten einer Nachricht ankommen. Ein Sender mitten in einer großen Nachricht kann zwischen zwei Fragmenten anhalten, einen
01 03 48 65 6c
89 05 48 65 6c 6c 6f
80 02 6c 6f
Der Empfänger verarbeitet den Ping für sich und kehrt dann zum Sammeln von “Hello” zurück. Daten-Frames dürfen sich nicht auf diese Weise abwechseln: Eine neue Text- oder Binärnachricht kann erst beginnen, wenn das letzte Fragment der aktuellen angekommen ist. Nur Steuerframes dürfen dazwischenfahren, und das ist ein Grund, warum ein Steuerframe selbst nie fragmentiert ist und bei 125 Bytes oder weniger bleibt.
Ein Ping kommt zwischen 01 03 48 65 6c und 80 02 6c 6f an. Ist das erlaubt?
Was der Lern-Codec tut
Fragmente zusammenzusetzen braucht einen Zustand, der über Frames hinweg besteht: den Opcode der laufenden Nachricht, die bisher gesammelten Payloads, eine Größengrenze für ihre Summe und die Regel, dass ein Steuerframe mittendrin ankommen darf. Der Lern-Codec tut das nicht. Das read_message, das du in Lektion 6 schreibst, lehnt jeden Frame mit nicht gesetztem FIN oder mit Opcode 0 ab und nennt für die Ablehnung einen
Kein Close-Code bedeutet “dieser Endpunkt setzt fragmentierte Nachrichten nicht zusammen”. 1003, nicht unterstützte Daten, ist die am wenigsten falsche Wahl: Er sagt, dass der Endpunkt nicht annehmen kann, was angekommen ist. 1002, Protokollfehler, wäre falsch, weil er der Gegenseite die Schuld gibt, und eine Gegenseite, die eine Nachricht fragmentiert, verletzt keine Regel. Das ist eine Grenze des Lern-Codecs, gewählt, damit der Codec klein genug bleibt, um ihn in einem Kapitel zu schreiben, kein Fehler der Gegenseite. Ein konformer
Die Bibliothek websockets, die du ab Kapitel 6 verwendest, akzeptiert sie. In einem Lauf, der beim Schreiben dieses Kurses gemacht wurde, haben Frames, die mit dem Lern-Codec gebaut waren, “Hel”, einen Ping und “lo” über
Warum schließt der Lern-Codec mit 1003, wenn er ein Fragment sieht?
Der Lern-Codec ist also keine vollständige WebSocket-Implementierung, und er sagt das mit einem Code, statt auf verwirrende Weise zu scheitern. Die nächste Lektion, das Projekt des Kapitels, stellt jede Regel dieses Kapitels hinter eine read_message, die einen Byte-Stream in Nachrichten und Steuerereignisse verwandelt und jeder Ablehnung ihren Close-Code gibt.