0%

Frames, Byte für Byte · Übung

Einen kleinen Frame kodieren

Das einfachste Beispiel des RFC ist eine ganze Textnachricht, “Hello”, in einem unmaskierten . Abschnitt 5.7 gibt diese sieben Bytes an, hier als Hex-Paare geschrieben:

81 05 48 65 6c 6c 6f

Die ersten beiden kannst du schon lesen: 81 ist gesetztes FIN mit 1, und 05 ist nicht gesetztes MASK mit einer Payload-Länge von 5. Die fünf Bytes danach sind die Buchstaben von “Hello” in . Diese Lektion schreibt die , die diesen Frame und Frames wie ihn aus einer Nachricht baut.

Text und binär

-Nachrichten gibt es in zwei Arten. Ein Text-Frame, Opcode 1, trägt Text, der als UTF-8 kodiert ist. Ein binärer Frame, Opcode 2, trägt beliebige Bytes, etwa ein Bild oder eine mit struct gepackte Zahl. Auf der Leitung sind beide nur eine Payload aus Bytes; der Opcode sagt dem Empfänger, wie er sie behandeln soll.

encode_frame(opcode, payload) akzeptiert beide Arten von Payload. Wenn payload ein str ist, kodiert die Funktion ihn zuerst mit UTF-8; wenn er schon bytes ist, verwendet sie ihn unverändert. isinstance(payload, str) unterscheidet die beiden. Also geben encode_frame(OP_TEXT, "Hello") und encode_frame(OP_TEXT, b"Hello") denselben Frame zurück.

Bytes zählen, nicht Zeichen

Die Länge in Byte 1 zählt Payload-Bytes, nicht Zeichen. Bei “Hello” stimmen die beiden Zahlen überein, aber wie Networks with Python, Kapitel 2, gezeigt hat, verwendet UTF-8 für viele Zeichen mehr als ein Byte. é braucht zwei Bytes und ✓ drei, also ist "café ✓" 6 Zeichen und 9 Bytes lang:

Try it

Der Frame beginnt mit 81 09: Das zweite Byte ist 9, die Länge dessen, was tatsächlich gesendet wird. Ein Frame, der 6 angäbe, würde drei Bytes zurücklassen, und der Empfänger würde sie als Anfang des nächsten Frames lesen.

encode_frame(OP_TEXT, "café ✓") baut einen Frame. Was ist sein zweites Byte?

Byte 0 und Byte 1 bauen

Das erste Byte kombiniert das FIN-Flag und den Opcode, mit den Werkzeugen aus Lektion 1. Beginne mit dem Opcode und setze dann mit | 0x80 das oberste Bit, wenn fin wahr ist. Der Opcode bleibt in den unteren vier Bits, wo parse_header_bytes nach ihm sucht.

fin ist ein reines Schlüsselwortargument mit dem Standardwert True, also baut ein gewöhnlicher Aufruf wie encode_frame(OP_TEXT, "Hello") einen finalen Frame, und nur ein Aufrufer, der fin=False schreibt, bekommt einen Frame, der nicht final ist. In diesem Kapitel brauchst du fin=False nicht, aber es zeigt, dass das Bit wirklich in deiner Hand liegt: encode_frame(OP_TEXT, "Hel", fin=False) ist 01 03 48 65 6c, das erste Fragment einer Nachricht, die der RFC in zwei Frames sendet.

Was gibt das aus?

print(hex(0x80 | 0x2), hex(0x0 | 0x1))

Das zweite Byte ist die Länge der Payload. bytes([b0, length]) verwandelt eine von Zahlen von 0 bis 255 in einen bytes- mit einem Byte pro Zahl, und + payload hängt die Payload dahinter an. Das ist der ganze Frame.

Vorerst nur kleine Frames

Eine 7-Bit-Länge reicht bis 127, und wie Lektion 2 gesagt hat, haben 126 und 127 eine andere Bedeutung. Also verarbeitet das encode_frame dieser Lektion Payloads von 0 bis 125 Bytes und lehnt alles Längere mit einem ab, der sagt, dass Lektion 4 die längeren Formen hinzufügt. Es lehnt außerdem einen Opcode ab, der nicht in KNOWN_OPCODES ist, weil kein Leser den Frame akzeptieren würde.

Deine Aufgabe

Schreibe in ws_frames.py die Funktion encode_frame(opcode, payload, *, fin=True):

  1. Löse ValueError aus, wenn opcode nicht in KNOWN_OPCODES ist;

  2. wenn payload ein str ist, kodiere ihn mit UTF-8;

  3. löse ValueError aus, wenn die Payload länger als 125 Bytes ist, mit einer Meldung, die Lektion 4 nennt;

  4. baue das erste Byte aus dem Opcode, mit 0x80 per | hinzugefügt, wenn fin wahr ist;

  5. gib das erste Byte, das Längenbyte und die Payload als einen bytes-Wert zurück.

Lass parse_header_bytes und die Platzhalter für spätere Lektionen, wie sie sind.

Klicke auf Run. Der Abschnitt zu Lektion 3 gibt deinen Frame für “Hello” aus, und er sollte genau den Bytes des RFC entsprechen. Der Abschnitt zu Lektion 4 führt deine Funktion jetzt ebenfalls aus, mit einer 65536 Byte langen Payload, und deine eigene Ablehnung erscheint dort, nach ValueError:. Das ist so beabsichtigt: frames_demo.py fängt den ValueError ab und gibt seine Meldung aus. Mit der Formulierung der Referenzlösung lautet die Ausgabe ab Lektion 3:

Lesson 3: a text frame for Hello
81 05 48 65 6c 6c 6f
Lesson 4: the start of a 65536-byte frame
  ValueError: payloads over 125 bytes need a longer length form (Lesson 4)
Lesson 5: Hello decoded one byte at a time
  not written yet

Klicke dann auf Submit. Die Prüfung vergleicht deine Frames mit Bytes, die sie selbst baut, für einen Text, den du nicht gesehen hast, mit Zeichen, die mehr als ein Byte brauchen, eine 125 Byte lange binäre Payload, einen leeren Text, einen Frame mit fin=False und einen , und sie erwartet ValueError für zwei reservierte Opcodes und eine 126 Byte lange Payload.

Deine Frames hören bei 125 Bytes auf. Die nächste Lektion entfernt diese Obergrenze mit den beiden längeren Längenformen.

Aufgabe

Schreibe encode_frame(opcode, payload, *, fin=True) in ws_frames.py. Sie gibt einen unmaskierten als bytes zurück:

  • ein erstes Byte, das den in seinen unteren vier Bits enthält, mit gesetztem oberstem Bit 0x80, wenn fin wahr ist;

  • ein zweites Byte, das die Länge der Payload in Bytes enthält;

  • die Payload, zuerst mit kodiert, wenn sie ein str ist, und unverändert verwendet, wenn sie bytes ist.

Löse aus bei einem Opcode, der nicht in KNOWN_OPCODES ist, und bei einer Payload, die länger als 125 Bytes ist, mit einer Meldung, die Lektion 4 nennt.

Klicke auf Run: Der Abschnitt zu Lektion 3 sollte 81 05 48 65 6c 6c 6f ausgeben, und der Abschnitt zu Lektion 4 gibt die Meldung deines ValueError aus. Klicke dann auf Submit. Die Prüfung vergleicht deine Frames mit Bytes, die sie selbst baut: “Hello” als str und als bytes, ein Text, den du nicht gesehen hast, dessen UTF-8 länger ist als seine Zeichenzahl, eine 125 Byte lange binäre Payload, ein leerer Text, fin=False und ein . Sie erwartet ValueError für Opcode 3, Opcode 0xB und eine 126 Byte lange Payload. Jede Art, die Bytes zusammenzufügen, ist in Ordnung.