Frames, byte voor byte · oefening
Drie manieren om te zeggen hoe lang
Zeven bits bevatten de getallen 0 tot en met 127, en een payload kan veel langer zijn dan dat. RFC 6455 lost dit op zonder elke header groter te maken. Lengtes van 0 tot en met 125 gaan rechtstreeks in het veld van 7 bits, zoals in les 3. De twee overgebleven
Hier zijn de headers op de grenzen, elke byte vóór de payload. De eerste vier zijn tekstframes en de rest binair, wat alleen de eerste byte verandert:
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
Lees de rij voor 256 bytes: 7e is 126, de markering, en 01 00 is de lengte in twee bytes, 1 keer 256 plus 0. De rij voor 65536 bytes heeft de markering 127, 7f, en daarna acht bytes die 65536 bevatten, wat 1 keer 256 keer 256 is. De RFC zelf gebruikt die twee rijen als voorbeelden van de langere vormen.
struct, opnieuw
Twee bytes en acht bytes zijn klussen voor struct, dat je in Networks with Python hoofdstuk 8 gebruikte met het formaat "!I", één unsigned geheel getal van 4 bytes in "!H" is één unsigned geheel getal in 2 bytes, 0 tot en met 65535, en "!Q" is er een in 8 bytes. De ! betekent nog steeds netwerkbytevolgorde, big-endian, meest significante byte eerst, en de RFC eist precies die volgorde. struct.unpack geeft nog steeds een [0]:
Wat drukt dit af?
import struct
print((bytes([0x81, 126]) + struct.pack("!H", 127)).hex(" "))
Altijd de kortste vorm
Niets in de bytes houdt een afzender tegen om de lengte 5 te schrijven als de markering 126 en daarna 00 05. RFC 6455 sluit het uit: sectie 5.2 zegt dat het minimale aantal bytes moet worden gebruikt om de lengte te coderen. Elke lengte heeft dus precies één header, en een lezer weigert elke andere. Deze drie worden geweigerd:
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
De derde regel staat los van de rest. De RFC eist dat de hoogste bit van de lengte van 8 bytes 0 is, dus de grootste lengte die een
Een frame zegt lengte 126 en daarna 00 7d. Wat moet een decoder doen?
De functie read_length
Coderen is één read_length(length, read_exact) twee parse_header_bytes en een functie om meer bytes mee te lezen.
read_exact(n) is de functie die je in Networks with Python hoofdstuk 6, les 3, schreef als read_exact(reader, n): hij geeft precies n bytes terug, hoe de verbinding ze ook aflevert, houdt extra bytes in een buffer, en werpt ConnectionError op als de peer eerst sluit. Hier komt hij binnen als read_length nooit hoeft te weten waar de bytes vandaan komen. Een echte verbinding, een test en frames_demo.py kunnen hem elk hun eigen read_exact geven. Bijvoorbeeld met een stand-in uit fakes.py van het hoofdstuk die de bytes 01 00 bevat:
read_exact = fakes.reader_over([bytes([1, 0])])
# reads 2 bytes and returns 256
ws_frames.read_length(126, read_exact)
Voor een lengte van 125 of minder leest read_length niets en geeft het de lengte zelf terug. Voor 126 leest het 2 bytes en pakt die uit met "!H"; voor 127 8 bytes met "!Q". Daarna controleert het de regels hierboven voordat het iets teruggeeft.
Je opdracht
In ws_frames.py:
Schrijf
length_field(n). Geefbytes([n])terug voor 0 tot en met 125; de byte 126 gevolgd doorstruct.pack("!H", n)voor 126 tot en met 65535; en de byte 127 gevolgd doorstruct.pack("!Q", n)daarboven.Schrijf
read_length(length, read_exact). Geeflengthterug als die 125 of minder is, zonder te lezen. Lees na 126 2 bytes en werpop als het getal 125 of minder is. Lees na 127 8 bytes en werp ValueErrorop als de hoogste bit gezet is (het getal is groter dan0x7FFFFFFFFFFFFFFF) of als het getal 65535 of minder is.Pas
encode_frameaan: laat de limiet van 125 bytes vallen en bouw de lengtebytes metlength_field(len(payload)). Al het andere blijft zoals les 3 het schreef.
Druk op Run. De sectie van les 4 drukt geen weigering meer af. Ze toont de eerste tien bytes van een binair frame van 65536 bytes, de header van de RFC:
Lesson 4: the start of a 65536-byte frame
82 7f 00 00 00 00 00 01 00 00
Druk daarna op Submit. De controle vergelijkt length_field met bytes die met struct zijn gebouwd op elke grens en bij lengtes die je nog niet hebt gezien, controleert de headers van 256 bytes en 65536 bytes van de RFC uit encode_frame samen met de kleine frames van les 3, en geeft read_length een stand-in-read_exact die noteert om hoeveel bytes er gevraagd werd. Om meer vragen dan de vorm nodig heeft, faalt, en een van de drie geweigerde headers accepteren ook.
De codec kan nu elke lengte schrijven en lezen. De volgende les zet de leesonderdelen samen in decode_frame, dat één heel frame uit een stroom leest.
Opdracht
In ws_frames.py:
Schrijf
length_field(n): de lengtebytes voor een payload vannbytes in de kortste vorm.bytes([n])voor 0 tot en met 125; de byte 126 en daarnastruct.pack("!H", n)voor 126 tot en met 65535; de byte 127 en daarnastruct.pack("!Q", n)daarboven.Schrijf
read_length(length, read_exact): geeflengthzelf terug als die 125 of minder is, zonder iets te lezen. Als die 126 is, lees je 2 bytes metread_exact(2)en pak je ze uit met"!H"; als die 127 is, lees je 8 bytes en pak je ze uit met"!Q". Werpop voor een lengte die een kortere vorm had moeten gebruiken (125 of minder na 126, 65535 of minder na 127) en voor een lengte van 8 bytes met de hoogste bit gezet. Pas
encode_framezo aan dat het geen limiet van 125 bytes heeft en zijn lengtebytes bouwt metlength_field(len(payload)).
Druk op Run: de sectie van les 4 moet 82 7f 00 00 00 00 00 01 00 00 afdrukken. Druk daarna op Submit. De controle vergelijkt length_field met bytes die ze met struct bouwt, op elke grens en bij lengtes die je nog niet hebt gezien; controleert de headers van 256 bytes en 65536 bytes van de RFC en de kleine encode_frame; en geeft read_length een stand-in-read_exact die elk aantal noteert waarom gevraagd wordt. read_length moet om niets, precies 2 of precies 8 bytes vragen, en moet een 16-bits 125 of 0, een 64-bits 65535, en een 64-bits lengte met de hoogste bit gezet weigeren. int.to_bytes en int.from_bytes met "big" zijn ook goed in plaats van struct.