Skip to main content

multiprocessing.connection

cpython 3.14 @ ab2d84fe1023/Lib/multiprocessing/connection.py

IPC connection objects for passing Python values between processes. The module wraps file descriptors and sockets with a framing protocol: send() pickles an object and writes a 4-byte length prefix, recv() reads that length then unpickles. Pipe() returns a pair of connected Connection objects backed by OS pipes or sockets. Client() and Listener() layer a socket-based rendezvous on top. Authentication between client and server uses HMAC-SHA256 over a random nonce produced by os.urandom.

Reading​

Connection: send, recv, and the framing protocol​

Every Connection wraps a single file descriptor. send() pickles the caller's object with pickle.dumps, prepends a 4-byte big-endian length, and calls the internal _send_bytes helper, which loops until the entire buffer is written. recv() mirrors this: _recv_bytes reads exactly 4 bytes for the header, then reads exactly that many payload bytes, and recv() unpickles the result.

# Lib/multiprocessing/connection.py (CPython 3.14, simplified)
def send(self, obj):
self._check_closed()
self._check_writable()
self._send_bytes(_ForkingPickler.dumps(obj))

def recv(self):
self._check_closed()
self._check_readable()
buf = self._recv_bytes()
return _ForkingPickler.loads(buf.getbuffer())

def _send_bytes(self, buf):
n = len(buf)
if n > 0x7fffffff:
raise ValueError("message too long")
# 4-byte length header followed by payload
self._send(struct.pack("!i", n))
self._send(buf)

The !i format sends a signed 32-bit big-endian integer, so the maximum single message size is 2 GiB. Anything larger raises ValueError before any bytes are written.

Listener and Client: socket-based rendezvous​

Listener binds a server socket and accepts one connection at a time via accept(). The address family is inferred from the address type: a string that starts with / (or any string on Windows) uses AF_UNIX; a (host, port) tuple uses AF_INET. Client() connects to an existing Listener address and returns a Connection.

# Listener.accept() -- waits for one incoming connection
def accept(self):
self._check_closed()
s, self._last_accepted = self._listener.accept()
s.setblocking(True)
c = Connection(s.detach())
if self._authkey is not None:
answer_challenge(c, self._authkey)
deliver_challenge(c, self._authkey)
return c

If an authkey was supplied to Listener, both sides run the challenge-response protocol immediately after the TCP/Unix handshake, before the caller's accept() call returns.

Authentication: HMAC challenge-response​

The authentication protocol is symmetric. The server sends a random 20-byte nonce (os.urandom(20)). The client signs the nonce with HMAC-SHA256 keyed by the shared authkey and sends the digest back. The server verifies it, then the roles reverse so the client authenticates the server.

MESSAGE_LENGTH = 20
CHALLENGE = b"#CHALLENGE#"
WELCOME = b"#WELCOME#"
FAILURE = b"#FAILURE#"

def deliver_challenge(connection, authkey):
import hmac
assert isinstance(authkey, bytes)
message = os.urandom(MESSAGE_LENGTH)
connection.send_bytes(CHALLENGE + message)
digest = hmac.new(authkey, message, "md5").digest()
response = connection.recv_bytes(256) # reject oversized replies
if response == digest:
connection.send_bytes(WELCOME)
else:
connection.send_bytes(FAILURE)
raise AuthenticationError("digest mismatch")

The nonce prevents replay attacks. Both sides must complete the exchange or the connection is torn down before any user data flows.

gopy mirror​

Not yet ported. The Go standard library has net.Conn and os.Pipe, which cover the transport layer. A port would need to implement the framing protocol (_send_bytes / _recv_bytes), the HMAC challenge-response handshake, and wait() (a cross-platform select/poll wrapper).

CPython 3.14 changes​

  • wait() on Windows now uses WaitForMultipleObjects directly rather than a thread-per-handle workaround introduced in 3.3.
  • Connection.send_bytes / recv_bytes accept a memoryview without copying when the underlying fd supports sendmsg.
  • The default pickle protocol used by _ForkingPickler tracks pickle.DEFAULT_PROTOCOL, which is 5 in 3.14.