reliable-transport/packet.py
2026-10-19 13:24:25 -06:00

61 lines
2.2 KiB
Python

"""The packet format every sender and receiver in this project uses. Given; do not change it.
0 1 5 7 9
+------+--------------+--------+----------+-----------------+
| type | seq (32) | len | checksum | payload (len) |
+------+--------------+--------+----------+-----------------+
All header fields are big-endian ("network order"). The checksum is the
Internet checksum (RFC 1071) over the whole packet with the checksum field set
to zero, the same one IPv4, UDP and TCP use.
References:
https://docs.python.org/3/library/struct.html
https://www.rfc-editor.org/rfc/rfc1071
"""
import struct
DATA = 0 # carries payload; seq numbers the packet, starting at 0
ACK = 1 # acknowledges; what seq means depends on the protocol (see README)
FIN = 2 # the sender is done; seq is the number of data packets it sent
FINACK = 3 # the receiver has everything and is closing
HEADER = struct.Struct("!BIHH")
MSS = 1000 # most payload bytes in one packet
def _checksum(data):
if len(data) % 2:
data += b"\0"
total = sum(struct.unpack(f"!{len(data) // 2}H", data))
while total >> 16:
total = (total & 0xFFFF) + (total >> 16)
return ~total & 0xFFFF
def encode(kind, seq, payload=b""):
"""Build one packet as bytes."""
if len(payload) > MSS:
raise ValueError(f"payload of {len(payload)} bytes is over the MSS of {MSS}")
header = HEADER.pack(kind, seq, len(payload), 0)
check = _checksum(header + payload)
return HEADER.pack(kind, seq, len(payload), check) + payload
def decode(raw):
"""Return (kind, seq, payload), or None if the packet is short or corrupted."""
if len(raw) < HEADER.size:
return None
kind, seq, length, check = HEADER.unpack_from(raw)
payload = raw[HEADER.size:]
if length != len(payload) or kind not in (DATA, ACK, FIN, FINACK):
return None
if _checksum(HEADER.pack(kind, seq, length, 0) + payload) != check:
return None
return kind, seq, payload
def split(data):
"""Cut a byte string into MSS-sized payloads, in order."""
return [data[i:i + MSS] for i in range(0, len(data), MSS)]