Frames, byte voor byte · oefening
De eerste twee bytes lezen
Elk 81 05 48 65 6c 6c 6f. Lees eerst de twee rijen bits: elk vakje is één bit, en de labels erboven zeggen bij welk veld de bit hoort.

In woorden is een frame, op volgorde:
byte 0: FIN, RSV1, RSV2 en RSV3, elk één bit, daarna een
van 4 bits; byte 1: MASK, één bit, daarna een payloadlengte van 7 bits;
nog 2 lengtebytes, alleen als de lengte van 7 bits 126 is, of nog 8 lengtebytes, alleen als die 127 is;
een
van 4 bytes, alleen als MASK 1 is; als laatste de payload.
In het “Hello”-frame is byte 0 0x81: FIN 1, de gereserveerde bits 0, en opcode 1, tekst. Byte 1 is 0x05: MASK 0 en lengte 5. Er volgt geen langere lengte en geen maskeersleutel, dus de vijf payloadbytes 48 65 6c 6c 6f, “Hello”, komen als volgende.
Byte 0 is de byte die je in les 1 uit elkaar haalde. Byte 1 bevat nog een vlag en een getal van 7 bits. Dat getal is een lengteprefix, hetzelfde idee als de header van 4 bytes in Networks with Python hoofdstuk 8: het vertelt de lezer hoeveel payloadbytes er volgen. Zeven bits komen echter maar tot 127, dus twee van de
Byte 0
De hoogste bit, 0x80, is FIN, kort voor final (laatste). Een gezette FIN-bit betekent dat dit frame het laatste stuk van zijn bericht is. Een groot bericht kan als meerdere frames worden gestuurd, fragmenten genoemd, met FIN uit op alle behalve het laatste. Elk bericht in dit hoofdstuk past in één frame, dus FIN is gezet in de frames die je bouwt, behalve één keer in les 3, waar je hem uitzet om te zien dat de bit van jou is; hoofdstuk 4 stuurt hele berichten in fragmenten.
De volgende drie bits, 0x40, 0x20 en 0x10, zijn RSV1, RSV2 en RSV3, de gereserveerde bits. Ze zijn vrij voor extensies waar beide kanten het tijdens de (b0 >> 4) & 0x07 schuift ze naar onderen en houdt alleen die drie, wat een getal van 0 tot en met 7 geeft dat 0 moet zijn.
De laagste vier bits, b0 & 0x0F, zijn de opcode: het soort frame. RFC 6455 definieert zes van de zestien mogelijke waarden:
| opcode | frame |
|---|---|
0x0 | vervolgframe, een later stuk van een gefragmenteerd bericht |
0x1 | tekst, een bericht in |
0x2 | binair, een bericht van willekeurige bytes |
0x3 tot en met 0x7 | gereserveerd |
0x8 | close |
0x9 | |
0xA | pong |
0xB tot en met 0xF | gereserveerd |
De opcodes vanaf 0x8 zijn controleframes: ze beheren de verbinding in plaats van een bericht te dragen, en hoofdstuk 4 gaat over ze. Gereserveerde opcodes zijn opzijgezet voor toekomstige versies van het protocol. Geen enkele peer mag er vandaag een sturen, dus een lezer weigert elk frame dat er een gebruikt. ws_frames.py somt de zes gedefinieerde opcodes al op in KNOWN_OPCODES.
Een frame begint met de byte 0x89. Wat is het?
Waarom moet parse_header_bytes een frame weigeren waarvan de eerste byte 0x83 is?
Byte 1
De hoogste bit van byte 1, 0x80, is MASK. Als die gezet is, volgt er na de lengte een maskeersleutel van 4 bytes, en is de payload ermee door elkaar gehusseld. Clients maskeren elk frame dat ze sturen en servers doen dat nooit; hoofdstuk 4 legt uit waarom. Voorlopig meldt je parser alleen de vlag.
De andere zeven bits, b1 & 0x7F, zijn de payloadlengte. Het masker 0x7F is 01111111: het houdt zeven bits en wist MASK, dus een gemaskeerd frame van 5 bytes, 0x85, leest nog steeds als lengte 5. Zoals les 1 liet zien, bindt & sterker dan !=, dus b1 & 0x80 != 0 is al de vergelijking (b1 & 0x80) != 0, en die is True of False.
Wat drukt dit af?
b1 = 0x85
print(b1 & 0x80 != 0, b1 & 0x7F)
Je bytes-waarde, en indexeren in een bytes-waarde geeft het getal in die byte, dus voor een frame frame is frame[0] b0 en frame[1] b1. Hier is een klein binair frame met een payload van 3 bytes, zo gelezen:
not bindt losser dan &, dus not b1 & 0x80 vraagt of de MASK-bit uit staat. Het programma van dit hoofdstuk roept je functie op dezelfde manier aan, op het “Hello”-frame van de RFC.
Je opdracht
Schrijf in ws_frames.py parse_header_bytes(b0, b1). Geef een
"fin":Trueals de hoogste bit vanb0gezet is, andersFalse;"rsv": de drie gereserveerde bits als getal,(b0 >> 4) & 0x07;"opcode": de laagste vier bits vanb0;"masked":Trueals de hoogste bit vanb1gezet is, andersFalse;"length": de laagste zeven bits vanb1.
Werp KNOWN_OPCODES staat; een "fin" en "masked" echte True- of False-waarden, geen 128 of 0: vergelijk met 0 zoals hierboven.
Druk op Run. De sectie van les 2 leest nu de eerste twee bytes van het tekstframe van de RFC voor “Hello”:
Lesson 2: the first two bytes of Hello
{'fin': True, 'rsv': 0, 'opcode': 1, 'masked': False, 'length': 5}
Een laatste tekstframe, niet gemaskeerd, met nog 5 bytes payload te gaan. Druk daarna op Submit. De controle roept je functie aan op frameheaders die je nog niet hebt gezien, waaronder een gemaskeerde, een die niet de laatste is, en elke gereserveerde bit en verscheidene gereserveerde opcodes apart.
Je kunt nu de header van een frame lezen. De volgende les schrijft er een: encode_frame bouwt het “Hello”-frame waar deze twee bytes vandaan kwamen.
Opdracht
Schrijf parse_header_bytes(b0, b1) in ws_frames.py. Het neemt de eerste twee bytes van een
"fin":Trueals de hoogste bit vanb0(0x80) gezet is, andersFalse;"rsv": de drie gereserveerde bits als getal,(b0 >> 4) & 0x07;"opcode": de laagste vier bits vanb0;"masked":Trueals de hoogste bit vanb1gezet is, andersFalse;"length": de laagste zeven bits vanb1.
Werp KNOWN_OPCODES staat.
Druk op Run om de sectie van les 2 te zien, en druk daarna op Submit. De controle roept parse_header_bytes aan met headerbytes die je nog niet hebt gezien: frames die wel en niet de laatste zijn, gemaskeerd en niet gemaskeerd, met de lengtes 0, 125, 126 en 127, elke gereserveerde bit apart gezet, en verscheidene gereserveerde opcodes. "fin" en "masked" moeten True of False zijn. Je dict mag extra sleutels bevatten. Laat de andere ws_frames.py zoals ze zijn.