Frames, byte voor byte · oefening
Een frame uit een stroom decoderen
encode_frame zet een bericht om in bytes. De ontvangende kant heeft het omgekeerde nodig: bytes van de verbinding nemen en het bericht terugkrijgen. Het probleem is er een dat je in Networks with Python hoofdstuk 6 oploste. recv één frame bevat, werkt op een rustige testmachine en faalt op een druk netwerk.
Het antwoord uit Networks geldt nog steeds: vraag de verbinding nooit “wat heb je?”, vraag haar “geef me precies n bytes”. Een frame vertelt je, stap voor stap, om hoeveel bytes je daarna moet vragen.
read_exact als parameter
decode_frame(read_exact, *, max_size=MAX_SIZE) krijgt zijn bytes alleen via read_exact, dezelfde soort read_length aanneemt. Het ziet nooit een fakes.py. Open het en lees de twee functies één keer:
pieces(data, size)kniptdatain eenstukken van hooguit sizebytes, zoals een drukke verbinding ze zou kunnen afleveren.reader_over(chunks)geeft een functieread_exact(n)terug die stukken op volgorde uit de lijst neemt, ze in een buffer houdt totdat hijnbytes heeft, precies die teruggeeft, en de rest bewaart voor de volgende aanroep. Als de stukken eerder op zijn, werpt hijConnectionErrorop, zoals een gesloten verbinding zou doen.
Dus fakes.reader_over(fakes.pieces(frame, 1)) is een read_exact over een verbinding die frame één byte per keer aflevert. Dat is wat frames_demo.py je decoder geeft in zijn sectie voor les 5. De stukken van één byte bewijzen alleen dat je decoder om exacte aantallen vraagt en nooit aanneemt dat er meer is aangekomen; de stukken tot die aantallen verzamelen is de taak van de reader, dezelfde ontvangstbuffer die je in Networks schreef.
Op volgorde
Een decoder leest een frame in de volgorde waarin de header het beschrijft:
read_exact(2), de twee bytes die elk frame heeft, dieparse_header_bytesleest;read_length(...), dat nog 0, 2 of 8 bytes leest, afhankelijk van de lengte van 7 bits;read_exact(length), de payload.
Elke stap heeft de vorige nodig om te weten om hoeveel hij moet vragen, dus de volgorde ligt vast. Om meer dan één stap tegelijk vragen, zoals 14 bytes om de langste header te dekken, zou eeuwig wachten op een frame van 7 bytes zoals het “Hello” van de RFC, of het begin van het volgende frame opslokken. Precies één frame lezen betekent ook dat een tweede frame er vlak achter intact in de buffer blijft, voor de volgende aanroep.
decode_frame heeft de eerste twee bytes 82 7e gelezen. Waar vraagt het daarna om?
Weigeren vóór het lezen
max_size is de grootste payload die deze decoder accepteert, standaard MAX_SIZE, een miljoen bytes. Networks with Python hoofdstuk 8 legde dezelfde soort limiet op zijn frames, om dezelfde reden. Met de lengte van 64 bits kan een peer een payload van miljarden bytes aankondigen, en een programma dat die probeerde te lezen, zou wachten op bytes die misschien nooit komen en meer geheugen vasthouden dan het heeft.
De header zegt al hoe groot de payload is. Vergelijk de lengte dus met max_size zodra read_length iets teruggeeft, en werp read_exact om ook maar één payloadbyte vraagt. Controleren nadat de payload is aangekomen, is te laat: het wachten en het geheugen zijn dan al besteed.
Waarom vergelijkt decode_frame de lengte met max_size voordat het de payload leest?
Een gemaskeerd frame
Nog één weigering. Sectie 5.7 van de RFC toont “Hello” zoals een client het zou sturen, gemaskeerd:
81 85 37 fa 21 3d 7f 9f 4d 51 58
De tweede byte, 85, heeft MASK gezet, en de vier bytes erna zijn de ValueError op voor elk frame waarvan het veld "masked" True is. Hoofdstuk 4 leert maskeren en geeft de decoder ook de kant van de server.
Je opdracht
Schrijf in ws_frames.py decode_frame(read_exact, *, max_size=MAX_SIZE):
lees 2 bytes en geef ze aan
parse_header_bytes, datValueErroropwerpt voor gereserveerde bits en; werp
ValueErrorop als het frame gemaskeerd is;haal de payloadlengte op met
read_length;werp
ValueErrorop als de lengte groter is danmax_size, voordat je enige payload leest;lees de payload en geef
{"fin": ..., "opcode": ..., "payload": ...}terug, met de payload alsbytes.
Vang ConnectionError niet. Als de peer midden in een frame sluit, moet de aanroeper dat weten.
Druk op Run. De sectie van les 5 decodeert het “Hello” van de RFC, dat één byte per keer aan je decoder wordt gegeven:
Lesson 5: Hello decoded one byte at a time
{'fin': True, 'opcode': 1, 'payload': b'Hello'}
Druk daarna op Submit. De controle geeft je decoder een stand-in-read_exact die elk aantal noteert waarom gevraagd wordt. Ze decodeert de frames van de RFC, een fragment, binaire frames van 256 en 65536 bytes met payloads die je nog niet hebt gezien, en twee frames die direct na elkaar worden gestuurd; ze controleert dat een te groot frame wordt geweigerd voordat om de payload gevraagd wordt, dat een gemaskeerd frame, een gereserveerde opcode en een niet-minimale lengte ValueError opwerpen, en dat een sluiting midden in een frame de aanroeper bereikt als ConnectionError.
De leercodec is af voor niet-gemaskeerde frames. Het project dat dit hoofdstuk afsluit, schrijft de tests die hem aan de bytes van de RFC houden.
Opdracht
Schrijf decode_frame(read_exact, *, max_size=MAX_SIZE) in ws_frames.py. Het leest één niet-gemaskeerd read_exact en geeft {"fin": ..., "opcode": ..., "payload": ...} terug:
lees 2 bytes en geef ze aan
parse_header_bytes;werp
op als het frame gemaskeerd is; haal de payloadlengte op met
read_length;werp
ValueErrorop als de lengte groter is danmax_size, voordat je om enige payloadbyte vraagt;lees precies de payload, en geef hem terug als
bytesmetfinen de.
Laat een ConnectionError uit read_exact de aanroeper bereiken.
Druk op Run: de sectie van les 5 moet {'fin': True, 'opcode': 1, 'payload': b'Hello'} afdrukken. Druk daarna op Submit. De controle geeft decode_frame een stand-in-read_exact die elk aantal noteert waarom gevraagd wordt. Ze verwacht dat de tekst- en pingframes van de RFC, een fragment, een leeg close-frame, binaire frames van 256 en 65536 bytes, en twee frames achter elkaar worden gedecodeerd, met de verzoeken 2, dan de lengtebytes, dan de payload, en niets gelezen voorbij het frame. Ze verwacht ValueError voor een frame groter dan max_size zonder verzoek om zijn payload, een gemaskeerd frame, een gereserveerde opcode en een niet-minimale lengte, en ConnectionError als de bytes midden in een frame ophouden. Je