Frames, Byte für Byte · Übung
Die ersten beiden Bytes lesen
Jeder 81 05 48 65 6c 6c 6f. Lies zuerst die beiden Bitzeilen: Jedes Kästchen ist ein Bit, und die Beschriftungen darüber sagen, zu welchem Feld das Bit gehört.

In Worten besteht ein Frame der Reihe nach aus:
Byte 0: FIN, RSV1, RSV2 und RSV3, je ein Bit, dann ein 4-Bit-
; Byte 1: MASK, ein Bit, dann eine 7-Bit-Payload-Länge;
2 weiteren Längenbytes, nur wenn die 7-Bit-Länge 126 ist, oder 8 weiteren Längenbytes, nur wenn sie 127 ist;
einem 4 Byte langen
, nur wenn MASK 1 ist; zuletzt der Payload.
Im “Hello”-Frame ist Byte 0 0x81: FIN 1, die reservierten Bits 0 und Opcode 1, Text. Byte 1 ist 0x05: MASK 0 und Länge 5. Es folgen keine längere Länge und kein Maskierungsschlüssel, also kommen als Nächstes die fünf Payload-Bytes 48 65 6c 6c 6f, “Hello”.
Byte 0 ist das Byte, das du in Lektion 1 auseinandergenommen hast. Byte 1 enthält ein weiteres Flag und eine 7-Bit-Zahl. Diese Zahl ist ein Längenpräfix, dieselbe Idee wie der 4 Byte lange Header in Networks with Python, Kapitel 8: Sie sagt dem Leser, wie viele Payload-Bytes folgen. Sieben Bits reichen aber nur bis 127, also bedeuten zwei ihrer
Byte 0
Das oberste Bit, 0x80, ist FIN, kurz für final. Ein gesetztes FIN-Bit bedeutet, dass dieser Frame das letzte Stück seiner Nachricht ist. Eine große Nachricht kann als mehrere Frames gesendet werden, sogenannte Fragmente, bei denen FIN bei allen außer dem letzten nicht gesetzt ist. Jede Nachricht in diesem Kapitel passt in einen Frame, also ist FIN in den Frames, die du baust, gesetzt, außer einmal in Lektion 3, wo du es löschst, um zu sehen, dass das Bit in deiner Hand liegt; Kapitel 4 sendet ganze Nachrichten in Fragmenten.
Die nächsten drei Bits, 0x40, 0x20 und 0x10, sind RSV1, RSV2 und RSV3, die reservierten Bits. Sie sind frei für Erweiterungen, auf die sich beide Seiten während des (b0 >> 4) & 0x07 schiebt sie nach ganz unten und behält nur diese drei, was eine Zahl von 0 bis 7 ergibt, die 0 sein muss.
Die unteren vier Bits, b0 & 0x0F, sind der Opcode: die Art des Frames. RFC 6455 definiert sechs der sechzehn möglichen Werte:
| Opcode | Frame |
|---|---|
0x0 | Fortsetzung, ein späteres Stück einer fragmentierten Nachricht |
0x1 | Text, eine Nachricht in |
0x2 | binär, eine Nachricht aus beliebigen Bytes |
0x3 bis 0x7 | reserviert |
0x8 | Close |
0x9 | |
0xA | Pong |
0xB bis 0xF | reserviert |
Die Opcodes ab 0x8 sind Steuerframes: Sie verwalten die Verbindung, statt eine Nachricht zu tragen, und um sie geht es in Kapitel 4. Reservierte Opcodes sind für zukünftige Versionen des Protokolls vorgesehen. Heute darf keine Gegenseite einen senden, also lehnt ein Leser jeden Frame ab, der einen verwendet. ws_frames.py führt die sechs definierten Opcodes bereits in KNOWN_OPCODES auf.
Ein Frame beginnt mit dem Byte 0x89. Was ist er?
Warum muss parse_header_bytes einen Frame ablehnen, dessen erstes Byte 0x83 ist?
Byte 1
Das oberste Bit von Byte 1, 0x80, ist MASK. Wenn es gesetzt ist, folgt auf die Länge ein 4 Byte langer Maskierungsschlüssel, und die Payload ist damit verwürfelt. Clients maskieren jeden Frame, den sie senden, und Server tun es nie; Kapitel 4 erklärt, warum. Vorerst meldet dein Parser nur das Flag.
Die anderen sieben Bits, b1 & 0x7F, sind die Payload-Länge. Die Maske 0x7F ist 01111111: Sie behält sieben Bits und löscht MASK, also liest sich ein maskierter Frame mit 5 Bytes, 0x85, trotzdem als Länge 5. Wie Lektion 1 gezeigt hat, bindet & stärker als !=, also ist b1 & 0x80 != 0 bereits der Vergleich (b1 & 0x80) != 0, und der ist True oder False.
Was gibt das aus?
b1 = 0x85
print(b1 & 0x80 != 0, b1 & 0x7F)
Deine bytes-Wert an, und das Indizieren eines bytes-Werts liefert die Zahl in diesem Byte, also ist für einen Frame frame frame[0] gleich b0 und frame[1] gleich b1. Hier ist ein kleiner binärer Frame mit einer 3 Byte langen Payload, auf diese Weise gelesen:
not bindet schwächer als &, also fragt not b1 & 0x80, ob das MASK-Bit nicht gesetzt ist. Das Programm dieses Kapitels ruft deine Funktion genauso auf, mit dem “Hello”-Frame des RFC.
Deine Aufgabe
Schreibe in ws_frames.py die Funktion parse_header_bytes(b0, b1). Gib ein
"fin":True, wenn das oberste Bit vonb0gesetzt ist, sonstFalse;"rsv": die drei reservierten Bits als Zahl,(b0 >> 4) & 0x07;"opcode": die unteren vier Bits vonb0;"masked":True, wenn das oberste Bit vonb1gesetzt ist, sonstFalse;"length": die unteren sieben Bits vonb1.
Löse KNOWN_OPCODES ist; ein "fin" und "masked" zu echten True- oder False-Werten, nicht zu 128 oder 0: Vergleiche mit 0 wie oben.
Klicke auf Run. Der Abschnitt zu Lektion 2 liest jetzt die ersten beiden Bytes des Text-Frames des RFC für “Hello”:
Lesson 2: the first two bytes of Hello
{'fin': True, 'rsv': 0, 'opcode': 1, 'masked': False, 'length': 5}
Ein finaler Text-Frame, unmaskiert, mit 5 Bytes Payload, die noch kommen. Klicke dann auf Submit. Die Prüfung ruft deine Funktion mit Frame-Headern auf, die du nicht gesehen hast, darunter ein maskierter, einer, der nicht final ist, und jedes reservierte Bit sowie mehrere reservierte Opcodes für sich allein.
Du kannst jetzt den Header eines Frames lesen. Die nächste Lektion schreibt einen: encode_frame baut den “Hello”-Frame, aus dem diese zwei Bytes stammen.
Aufgabe
Schreibe parse_header_bytes(b0, b1) in ws_frames.py. Sie nimmt die ersten beiden Bytes eines
"fin":True, wenn das oberste Bit vonb0(0x80) gesetzt ist, sonstFalse;"rsv": die drei reservierten Bits als Zahl,(b0 >> 4) & 0x07;"opcode": die unteren vier Bits vonb0;"masked":True, wenn das oberste Bit vonb1gesetzt ist, sonstFalse;"length": die unteren sieben Bits vonb1.
Löse KNOWN_OPCODES ist.
Klicke auf Run, um den Abschnitt zu Lektion 2 zu sehen, und klicke dann auf Submit. Die Prüfung ruft parse_header_bytes mit Header-Bytes auf, die du nicht gesehen hast: Frames, die final sind und die es nicht sind, maskiert und unmaskiert, mit den Längen 0, 125, 126 und 127, jedes reservierte Bit für sich gesetzt und mehrere reservierte Opcodes. "fin" und "masked" müssen True oder False sein. Dein Dict darf zusätzliche Schlüssel enthalten. Lass die anderen ws_frames.py, wie sie sind.