Frames, Byte für Byte · Übung
Einen kleinen Frame kodieren
Das einfachste Beispiel des RFC ist eine ganze Textnachricht, “Hello”, in einem unmaskierten
81 05 48 65 6c 6c 6f
Die ersten beiden kannst du schon lesen: 81 ist gesetztes FIN mit 05 ist nicht gesetztes MASK mit einer Payload-Länge von 5. Die fünf Bytes danach sind die Buchstaben von “Hello” in
Text und binär
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:
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 bytes-+ 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 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):
Löse
ValueErroraus, wennopcodenicht inKNOWN_OPCODESist;wenn
payloadeinstrist, kodiere ihn mit UTF-8;löse
ValueErroraus, wenn die Payload länger als 125 Bytes ist, mit einer Meldung, die Lektion 4 nennt;baue das erste Byte aus dem Opcode, mit
0x80per|hinzugefügt, wennfinwahr ist;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 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 bytes zurück:
ein erstes Byte, das den
in seinen unteren vier Bits enthält, mit gesetztem oberstem Bit 0x80, wennfinwahr ist;ein zweites Byte, das die Länge der Payload in Bytes enthält;
die Payload, zuerst mit
kodiert, wenn sie ein strist, und unverändert verwendet, wenn siebytesist.
Löse 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 ValueError für Opcode 3, Opcode 0xB und eine 126 Byte lange Payload. Jede Art, die Bytes zusammenzufügen, ist in Ordnung.