Frames, Byte by Byte · practice
Read the First Two Bytes
Every 81 05 48 65 6c 6c 6f. Read the two bit rows first: each box is one bit, and the labels above them say which field the bit belongs to.

In words, a frame is, in order:
byte 0: FIN, RSV1, RSV2 and RSV3, one bit each, then a 4-bit
; byte 1:
, one bit, then a 7-bit payload length; 2 more length bytes, only if the 7-bit length is 126, or 8 more length bytes, only if it is 127;
a 4-byte masking key, only if MASK is 1;
last, the payload.
In the “Hello” frame, byte 0 is 0x81: FIN 1, the reserved bits 0, and opcode 1, text. Byte 1 is 0x05: MASK 0 and length 5. No longer length and no masking key follow, so the five payload bytes 48 65 6c 6c 6f, “Hello”, come next.
Byte 0 is the byte you took apart in Lesson 1. Byte 1 holds one more flag and a 7-bit number. That number is a length prefix, the same idea as the 4-byte header in Networks with Python Chapter 8: it tells the reader how many payload bytes follow. Seven bits only reach 127, though, so two of its values mean something else, and Lesson 4 is about them. The masking key belongs to Chapter 4. This lesson reads the two bytes that every frame has.
Byte 0
The top bit, 0x80, is FIN, short for final. A set FIN bit means this frame is the last piece of its message. A large message may be sent as several frames, called fragments, with FIN clear on all but the last. Every message in this chapter fits in one frame, so FIN is set in the frames you build, except once in Lesson 3, where you clear it to see that the bit is yours; Chapter 4 sends whole messages in fragments.
The next three bits, 0x40, 0x20 and 0x10, are RSV1, RSV2 and RSV3, the reserved bits. They are free for extensions that both sides agree on during the (b0 >> 4) & 0x07 moves them to the bottom and keeps only those three, giving a number from 0 to 7 that must be 0.
The low four bits, b0 & 0x0F, are the opcode: the kind of frame. RFC 6455 defines six of the sixteen possible values:
| opcode | frame |
|---|---|
0x0 | continuation, a later piece of a fragmented message |
0x1 | text, a message in |
0x2 | binary, a message of any bytes |
0x3 to 0x7 | reserved |
0x8 | close |
0x9 | |
0xA | pong |
0xB to 0xF | reserved |
The opcodes from 0x8 up are control frames: they manage the connection instead of carrying a message, and Chapter 4 is about them. Reserved opcodes are set aside for future versions of the protocol. No peer may send one today, so a reader refuses any frame that uses one. ws_frames.py already KNOWN_OPCODES.
A frame starts with the byte 0x89. What is it?
Why must parse_header_bytes refuse a frame whose first byte is 0x83?
Byte 1
The top bit of byte 1, 0x80, is MASK. When it is set, a 4-byte masking key follows the length, and the payload is scrambled with it. Clients mask every frame they send and servers never do; Chapter 4 explains why. For now, your parser only reports the flag.
The other seven bits, b1 & 0x7F, are the payload length. The mask 0x7F is 01111111: it keeps seven bits and clears MASK, so a masked frame of 5 bytes, 0x85, still reads as length 5. As Lesson 1 showed, & binds tighter than !=, so b1 & 0x80 != 0 is already the comparison (b1 & 0x80) != 0, which is True or False.
What does this print?
b1 = 0x85
print(b1 & 0x80 != 0, b1 & 0x7F)
Your bytes bytes value gives the number in that byte, so for a frame frame, frame[0] is b0 and frame[1] is b1. Here is a small binary frame with a 3-byte payload, read that way:
not binds more loosely than &, so not b1 & 0x80 asks whether the MASK bit is clear. This chapter’s program calls your function the same way, on the RFC’s “Hello” frame.
Your task
In ws_frames.py, write parse_header_bytes(b0, b1). Return a
"fin":Trueif the top bit ofb0is set, elseFalse;"rsv": the three reserved bits as a number,(b0 >> 4) & 0x07;"opcode": the low four bits ofb0;"masked":Trueif the top bit ofb1is set, elseFalse;"length": the low seven bits ofb1.
Raise KNOWN_OPCODES; an "fin" and "masked" real True or False values, not 128 or 0: compare with 0 as above.
Press Run. The Lesson 2 section now reads the first two bytes of the RFC’s text frame for “Hello”:
Lesson 2: the first two bytes of Hello
{'fin': True, 'rsv': 0, 'opcode': 1, 'masked': False, 'length': 5}
A final text frame, unmasked, with 5 bytes of payload to come. Then press Submit. The check calls your function on frame headers you have not seen, including a masked one, one that is not final, and every reserved bit and several reserved opcodes on their own.
You can read a frame’s header now. The next lesson writes one: encode_frame builds the “Hello” frame these two bytes came from.
Task
Write parse_header_bytes(b0, b1) in ws_frames.py. It takes the first two bytes of a
"fin":Truewhen the top bit ofb0(0x80) is set, elseFalse;"rsv": the three reserved bits as a number,(b0 >> 4) & 0x07;"opcode": the low four bits ofb0;"masked":Truewhen the top bit ofb1is set, elseFalse;"length": the low seven bits ofb1.
Raise KNOWN_OPCODES.
Press Run to see the Lesson 2 section, then press Submit. The check calls parse_header_bytes with header bytes you have not seen: frames that are and are not final, masked and unmasked, with the lengths 0, 125, 126 and 127, each reserved bit set on its own, and several reserved opcodes. "fin" and "masked" must be True or False. Your dict may hold extra keys. Leave the other ws_frames.py as they are.