0%

Frames, Byte für Byte · Übung

Drei Arten, die Länge anzugeben

Sieben Bits fassen die Zahlen 0 bis 127, und eine Payload kann weit länger sein. RFC 6455 löst das, ohne jeden Header größer zu machen. Längen von 0 bis 125 kommen direkt in das 7-Bit-Feld, wie in Lektion 3. Die beiden übrigen sind gar keine Längen, sondern Markierungen: 126 bedeutet “die echte Länge folgt in den nächsten 2 Bytes”, und 127 bedeutet “die echte Länge folgt in den nächsten 8 Bytes”. Ein kleines Feld wählt zwischen drei Aufbauten.

Hier sind die Header an den Grenzen, jedes Byte vor der Payload. Die ersten vier sind Text- und der Rest binär, was nur das erste Byte ändert:

payload bytes   header
0               81 00
125             81 7d
126             81 7e 00 7e
127             81 7e 00 7f
256             82 7e 01 00
65535           82 7e ff ff
65536           82 7f 00 00 00 00 00 01 00 00

Lies die Zeile für 256 Bytes: 7e ist 126, die Markierung, und 01 00 ist die Länge in zwei Bytes, 1 mal 256 plus 0. Die Zeile für 65536 Bytes hat die Markierung 127, 7f, und dann acht Bytes, die 65536 enthalten, also 1 mal 256 mal 256. Der RFC selbst verwendet diese beiden Zeilen als seine Beispiele für die längeren Formen.

Wieder struct

Zwei Bytes und acht Bytes sind Aufgaben für struct, das du in Networks with Python, Kapitel 8, mit dem Format "!I" verwendet hast, einer vorzeichenlosen 4-Byte-Ganzzahl in . Diese Lektion braucht zwei andere Größen. "!H" ist eine vorzeichenlose Ganzzahl in 2 Bytes, 0 bis 65535, und "!Q" eine in 8 Bytes. Das ! bedeutet weiterhin Netzwerk-Byte-Reihenfolge, Big-Endian, das höchstwertige Byte zuerst, und der RFC verlangt genau diese Reihenfolge. struct.unpack gibt weiterhin ein zurück, also ist die Zahl das Element [0]:

Try it

Was gibt das aus?

import struct

print((bytes([0x81, 126]) + struct.pack("!H", 127)).hex(" "))

Immer die kürzeste Form

Nichts in den Bytes hindert einen Sender daran, die Länge 5 als Markierung 126 und dann 00 05 zu schreiben. RFC 6455 schließt das aus: Abschnitt 5.2 sagt, dass die minimale Anzahl von Bytes verwendet werden muss, um die Länge zu kodieren. Also hat jede Länge genau einen Header, und ein Leser lehnt jeden anderen ab. Diese drei werden abgelehnt:

81 7e 00 7d                     a 16-bit length of 125, which fits in 7 bits
82 7f 00 00 00 00 00 00 ff ff   a 64-bit length of 65535, which fits in 16 bits
82 7f 80 00 00 00 00 00 00 00   a 64-bit length with its top bit set

Die dritte Regel ist eine eigene. Der RFC verlangt, dass das oberste Bit der 8-Byte-Länge 0 ist, also ist die größte Länge, die ein Frame ankündigen kann, 2 hoch 63 minus 1. Das ist immer noch weit mehr, als irgendein Server akzeptieren sollte, und ein Leser, der es glaubte, würde auf eine Payload warten oder versuchen, eine Payload zu halten, die kein Programm verarbeiten könnte. Lektion 5 fügt aus diesem Grund eine Größengrenze hinzu.

Ein Frame gibt die Länge 126 an und dann 00 7d. Was sollte ein Decoder tun?

Die Funktion read_length

Kodieren ist eine einer Zahl. Dekodieren hat eine Schwierigkeit: Nach der 7-Bit-Länge braucht der Leser vielleicht 0, 2 oder 8 weitere Bytes, und er kann nicht wissen, wie viele, bis er Byte 1 gelesen hat. Also nimmt read_length(length, read_exact) zwei , den 7-Bit-Wert aus parse_header_bytes und eine Funktion, mit der weitere Bytes gelesen werden.

read_exact(n) ist die Funktion, die du in Networks with Python, Kapitel 6, Lektion 3, als read_exact(reader, n) geschrieben hast: Sie gibt genau n Bytes zurück, egal wie die Verbindung sie liefert, behält zusätzliche Bytes in einem Puffer und löst ConnectionError aus, wenn die Gegenseite vorher schließt. Hier kommt sie als an, eine Funktion, die als Wert übergeben wird, also muss read_length nie wissen, woher die Bytes kommen. Eine echte Verbindung, ein Test und frames_demo.py können ihr jeweils ihr eigenes read_exact übergeben. Zum Beispiel mit einem Stellvertreter aus der Datei fakes.py des Kapitels, der die Bytes 01 00 enthält:

read_exact = fakes.reader_over([bytes([1, 0])])
# reads 2 bytes and returns 256
ws_frames.read_length(126, read_exact)

Bei einer Länge von 125 oder weniger liest read_length nichts und gibt die Länge selbst zurück. Bei 126 liest sie 2 Bytes und entpackt sie mit "!H"; bei 127 8 Bytes mit "!Q". Dann prüft sie die Regeln oben, bevor sie zurückgibt.

Deine Aufgabe

In ws_frames.py:

  1. Schreibe length_field(n). Gib für 0 bis 125 bytes([n]) zurück; für 126 bis 65535 das Byte 126, gefolgt von struct.pack("!H", n); und darüber das Byte 127, gefolgt von struct.pack("!Q", n).

  2. Schreibe read_length(length, read_exact). Gib length zurück, wenn es 125 oder weniger ist, ohne zu lesen. Lies nach 126 2 Bytes und löse aus, wenn die Zahl 125 oder weniger ist. Lies nach 127 8 Bytes und löse ValueError aus, wenn das oberste Bit gesetzt ist (die Zahl über 0x7FFFFFFFFFFFFFFF liegt) oder wenn die Zahl 65535 oder weniger ist.

  3. Ändere encode_frame: Entferne die Grenze von 125 Bytes und baue die Längenbytes mit length_field(len(payload)). Alles andere bleibt so, wie Lektion 3 es geschrieben hat.

Klicke auf Run. Der Abschnitt zu Lektion 4 gibt keine Ablehnung mehr aus. Er zeigt die ersten zehn Bytes eines 65536 Byte langen binären Frames, den Header des RFC:

Lesson 4: the start of a 65536-byte frame
82 7f 00 00 00 00 00 01 00 00

Klicke dann auf Submit. Die Prüfung vergleicht length_field an jeder Grenze und bei Längen, die du nicht gesehen hast, mit Bytes, die mit struct gebaut sind, prüft die 256-Byte- und 65536-Byte-Header des RFC aus encode_frame zusammen mit den kleinen Frames aus Lektion 3 und übergibt read_length ein Stellvertreter-read_exact, das aufzeichnet, wie viele Bytes angefordert wurden. Mehr anzufordern, als die Form braucht, schlägt fehl, und einen der drei abgelehnten Header zu akzeptieren ebenso.

Der Codec kann jetzt jede Länge schreiben und lesen. Die nächste Lektion fügt die lesenden Teile zu decode_frame zusammen, das einen ganzen Frame aus einem Stream liest.

Aufgabe

In ws_frames.py:

  1. Schreibe length_field(n): die Längenbytes für eine n Bytes lange Payload in der kürzesten Form. bytes([n]) für 0 bis 125; das Byte 126 und dann struct.pack("!H", n) für 126 bis 65535; das Byte 127 und dann struct.pack("!Q", n) darüber.

  2. Schreibe read_length(length, read_exact): Gib length selbst zurück, wenn es 125 oder weniger ist, und lies nichts. Wenn es 126 ist, lies 2 Bytes mit read_exact(2) und entpacke sie mit "!H"; wenn es 127 ist, lies 8 Bytes und entpacke sie mit "!Q". Löse aus bei einer Länge, die eine kürzere Form hätte verwenden sollen (125 oder weniger nach 126, 65535 oder weniger nach 127), und bei einer 8-Byte-Länge mit gesetztem oberstem Bit.

  3. Ändere encode_frame so, dass es keine Grenze von 125 Bytes hat und seine Längenbytes mit length_field(len(payload)) baut.

Klicke auf Run: Der Abschnitt zu Lektion 4 sollte 82 7f 00 00 00 00 00 01 00 00 ausgeben. Klicke dann auf Submit. Die Prüfung vergleicht length_field mit Bytes, die sie mit struct baut, an jeder Grenze und bei Längen, die du nicht gesehen hast; prüft die 256-Byte- und 65536-Byte-Header des RFC und die kleinen aus Lektion 3 aus encode_frame; und gibt read_length ein Stellvertreter-read_exact, das jede angeforderte Anzahl aufzeichnet. read_length darf nichts, genau 2 oder genau 8 Bytes anfordern und muss eine 16-Bit-125 oder -0, eine 64-Bit-65535 und eine 64-Bit-Länge mit gesetztem oberstem Bit ablehnen. int.to_bytes und int.from_bytes mit "big" sind statt struct in Ordnung.