Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Python socket recv: assemble a bounded frame across partial reads

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A stream socket exposes an ordered byte stream, so one recv call does not identify an application message boundary.

Download Python source kit

Operation contract

The local socket pair carries a two-byte big-endian length followed by a payload. read_exact keeps receiving until its requested count is assembled or EOF rejects truncation. It intentionally requests at most two bytes per call, making the loop exercise partial application reads even on the local fixture. read_frame checks the declared size before requesting the body. A second pair contains a length of five but only two body bytes, then closes its sending direction; the receiver detects that short frame.

Failure and ownership boundary

A timeout on each receive call is not an absolute operation deadline: a peer can keep delivering tiny chunks before each timeout. The fixture uses already sent local bytes, no remote host, and a one-second socket timeout. Python struct: a fixed-endian receipt record with exact byte length, Python asyncio timeout: cancellation and cleanup ownership and Python bounded gzip decoding: cap expanded output before publishing it cover independent layers.

Working program

python
import socket

def read_exact(stream, count):
    chunks = bytearray()
    while len(chunks) < count:
        chunk = stream.recv(min(2, count - len(chunks)))
        if not chunk:
            raise EOFError("truncated frame")
        chunks.extend(chunk)
    return bytes(chunks)

def read_frame(stream):
    length = int.from_bytes(read_exact(stream, 2), "big")
    if length > 32:
        raise ValueError("frame cap")
    return read_exact(stream, length)

for wire in (b"\x00\x05R0041", b"\x00\x05R0", b"\x00\x21"):
    sender, receiver = socket.socketpair()
    with sender, receiver:
        receiver.settimeout(1)
        sender.sendall(wire)
        sender.shutdown(socket.SHUT_WR)
        try:
            print("payload:", read_frame(receiver))
        except (EOFError, ValueError) as failure:
            print(type(failure).__name__)

Output

Output
payload: b'R0041'
EOFError
ValueError

Costs and limits

The accepted body has at most 32 bytes, so this frame reader has bounded retained payload storage. Repeated frames still need a connection-wide count, byte budget and absolute deadline. A successful frame leaves following bytes for the next frame; it is not proof that the stream has no extra messages.

Common Mistakes

  • recv may return fewer bytes than requested without reaching EOF.
  • Check declared length before reading or allocating the body.

Connected lessons

Python struct: a fixed-endian receipt record with exact byte length, Python asyncio timeout: cancellation and cleanup ownership, Python JSON Lines: cap line bytes and validate the whole batch before returning it.

python
socket-frame-reading
Storage details