0%

Frames, byte voor byte · oefening

Een klein frame coderen

Het eenvoudigste voorbeeld van de RFC is een heel tekstbericht, “Hello”, in één niet-gemaskeerd . Sectie 5.7 geeft deze zeven bytes, hier geschreven als hexparen:

81 05 48 65 6c 6c 6f

De eerste twee kun je al lezen: 81 is FIN gezet met 1, en 05 is MASK uit met een payloadlengte van 5. De vijf bytes erna zijn de letters van “Hello” in . Deze les schrijft de die dat frame, en frames zoals dat, uit een bericht bouwt.

Tekst en binair

-berichten zijn er in twee soorten. Een tekstframe, opcode 1, draagt tekst gecodeerd als UTF-8. Een binair frame, opcode 2, draagt willekeurige bytes, zoals een afbeelding of een getal dat met 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:

Try it

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 met de standaardwaarde 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 getallen van 0 tot en met 255 om in een bytes- met één byte per getal, en + 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 die zegt dat les 4 de langere vormen toevoegt. Hij weigert ook een opcode die niet in KNOWN_OPCODES staat, omdat geen enkele lezer het frame zou accepteren.

Je opdracht

Schrijf in ws_frames.py encode_frame(opcode, payload, *, fin=True):

  1. werp ValueError op als opcode niet in KNOWN_OPCODES staat;

  2. als payload een str is, codeer hem dan met UTF-8;

  3. werp ValueError op als de payload langer is dan 125 bytes, met een melding die les 4 noemt;

  4. bouw de eerste byte uit de opcode, met 0x80 toegevoegd via | als fin waar is;

  5. 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 , en ze verwacht 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 terug als bytes:

  • een eerste byte met de in de laagste vier bits, met de hoogste bit 0x80 gezet als fin waar is;

  • een tweede byte met de lengte van de payload in bytes;

  • de payload, eerst gecodeerd met als het een str is, en gebruikt zoals hij is als het bytes is.

Werp op voor een opcode die niet in 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 . Ze verwacht ValueError voor opcode 3, opcode 0xB en een payload van 126 bytes. Elke manier om de bytes samen te voegen is goed.