Frames, byte voor byte · oefening
Een klein frame coderen
Het eenvoudigste voorbeeld van de RFC is een heel tekstbericht, “Hello”, in één niet-gemaskeerd
81 05 48 65 6c 6c 6f
De eerste twee kun je al lezen: 81 is FIN gezet met 05 is MASK uit met een payloadlengte van 5. De vijf bytes erna zijn de letters van “Hello” in
Tekst en binair
struct is ingepakt. Op de lijn zijn beide gewoon een payload van bytes; de opcode vertelt de ontvanger hoe hij ze moet behandelen.
encode_frame(opcode, payload) accepteert beide soorten payload. Als payload een str is, codeert de functie hem eerst met UTF-8; als hij al bytes is, gebruikt ze hem zoals hij is. isinstance(payload, str) houdt de twee uit elkaar. Dus encode_frame(OP_TEXT, "Hello") en encode_frame(OP_TEXT, b"Hello") geven hetzelfde frame terug.
Tel bytes, geen tekens
De lengte in byte 1 telt payloadbytes, geen tekens. Voor “Hello” komen de twee getallen overeen, maar, zoals Networks with Python hoofdstuk 2 liet zien, gebruikt UTF-8 voor veel tekens meer dan één byte. é kost twee bytes en ✓ drie, dus "café ✓" is 6 tekens en 9 bytes:
Het frame begint met 81 09: de tweede byte is 9, de lengte van wat er werkelijk wordt verstuurd. Een frame dat 6 zei, zou drie bytes achterlaten, en de ontvanger zou ze lezen als het begin van het volgende frame.
encode_frame(OP_TEXT, "café ✓") bouwt een frame. Wat is de tweede byte?
Byte 0 en byte 1 bouwen
De eerste byte combineert de FIN-vlag en de opcode, met het gereedschap uit les 1. Begin bij de opcode, en zet daarna de hoogste bit met | 0x80 als fin waar is. De opcode blijft in de laagste vier bits, waar parse_header_bytes hem zoekt.
fin is een keyword-only True, dus een gewone aanroep zoals encode_frame(OP_TEXT, "Hello") bouwt een laatste frame, en alleen een aanroeper die fin=False schrijft, krijgt een frame dat niet de laatste is. Je hebt fin=False in dit hoofdstuk niet nodig, maar het laat zien dat de bit echt van jou is om te zetten: encode_frame(OP_TEXT, "Hel", fin=False) is 01 03 48 65 6c, het eerste fragment van een bericht dat de RFC in twee frames stuurt.
Wat drukt dit af?
print(hex(0x80 | 0x2), hex(0x0 | 0x1))
De tweede byte is de lengte van de payload. bytes([b0, length]) zet een bytes-+ payload voegt de payload erachter toe. Dat is het hele frame.
Voorlopig alleen kleine frames
Een lengte van 7 bits komt tot 127, en zoals les 2 zei, hebben 126 en 127 een andere betekenis. Daarom verwerkt de encode_frame van deze les payloads van 0 tot en met 125 bytes en weigert hij alles wat langer is met een KNOWN_OPCODES staat, omdat geen enkele lezer het frame zou accepteren.
Je opdracht
Schrijf in ws_frames.py encode_frame(opcode, payload, *, fin=True):
werp
ValueErrorop alsopcodeniet inKNOWN_OPCODESstaat;als
payloadeenstris, codeer hem dan met UTF-8;werp
ValueErrorop als de payload langer is dan 125 bytes, met een melding die les 4 noemt;bouw de eerste byte uit de opcode, met
0x80toegevoegd via|alsfinwaar is;geef de eerste byte, de lengtebyte en de payload terug als één
bytes-waarde.
Laat parse_header_bytes en de stubs voor latere lessen zoals ze zijn.
Druk op Run. De sectie van les 3 drukt je frame voor “Hello” af, en dat moet precies overeenkomen met de bytes van de RFC. De sectie van les 4 draait je functie nu ook, op een payload van 65536 bytes, en je eigen weigering verschijnt daar, na ValueError:. Dat is de bedoeling: frames_demo.py vangt de ValueError en drukt de melding af. Met de formulering van de referentieoplossing luidt de uitvoer vanaf les 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
Druk daarna op Submit. De controle vergelijkt je frames met bytes die ze zelf bouwt, voor een tekst die je nog niet hebt gezien met tekens die meer dan één byte kosten, een binaire payload van 125 bytes, een lege tekst, een frame met fin=False, en een ValueError voor twee gereserveerde opcodes en een payload van 126 bytes.
Je frames stoppen bij 125 bytes. De volgende les haalt dat plafond weg met de twee langere lengtevormen.
Opdracht
Schrijf encode_frame(opcode, payload, *, fin=True) in ws_frames.py. Het geeft één niet-gemaskeerd bytes:
een eerste byte met de
in de laagste vier bits, met de hoogste bit 0x80gezet alsfinwaar is;een tweede byte met de lengte van de payload in bytes;
de payload, eerst gecodeerd met
als het een stris, en gebruikt zoals hij is als hetbytesis.
Werp KNOWN_OPCODES staat, en voor een payload die langer is dan 125 bytes, met een melding die les 4 noemt.
Druk op Run: de sectie van les 3 moet 81 05 48 65 6c 6c 6f afdrukken, en de sectie van les 4 drukt je ValueError-melding af. Druk daarna op Submit. De controle vergelijkt je frames met bytes die ze zelf bouwt: “Hello” als str en als bytes, een tekst die je nog niet hebt gezien waarvan de UTF-8 langer is dan het aantal tekens, een binaire payload van 125 bytes, een lege tekst, fin=False, en een ValueError voor opcode 3, opcode 0xB en een payload van 126 bytes. Elke manier om de bytes samen te voegen is goed.