cheatah
Module

socket

A small wrapper around BSD/POSIX TCP sockets, in the spirit of Python's socket module. Two layers are available: a flat, file-descriptor API (like the C layer — you pass the integer fd from socket() / tcp_listen() / accept() to the other calls), and, on top of it, owning guards (socket.open, socket.serve) that return Conn/Listener values whose destructors close the fd — use them with with so a connection or listener can't leak (see below). IPv4 + TCP only; hosts are resolved with getaddrinfo, so "localhost", "127.0.0.1", and DNS names all work.

import io
import socket

let lfd = socket.tcp_listen("127.0.0.1", 8080, 16)   # create + bind + listen
io.print("listening on", socket.local_port(lfd))
let conn = socket.accept(lfd)                         # wait for a client
let request = socket.recv(conn, 8192)
let nl = chr(13) + chr(10)                            # CRLF (no `\r` string escape)
socket.sendall(conn, "HTTP/1.1 200 OK" + nl + "Content-Length: 2" + nl + nl + "hi")
socket.close(conn)
socket.close(lfd)

What's here

  • Owning guards (RAII, with-friendly)open(host, port) → a Conn, and serve(host, port, backlog) → a Listener; each closes its fd on scope exit.

  • Conveniencetcp_listen(host, port, backlog), tcp_connect(host, port).

  • Per-connection I/Oaccept, recv, send, sendall, close, plus set_timeout(fd, ms) (recv/send deadlines) and shutdown(fd) (half-close).

  • Low-level BSDsocket, set_reuseaddr, bind, listen, connect, local_port, and last_error (the current errno text).

import io
import socket

with socket.serve("127.0.0.1", 8080, 16) as server {   # a Listener guard
    io.print("listening on", server.local_port())
    with server.accept() as conn {                     # a Conn guard for the client
        let request = conn.recv(8192)
        let nl = chr(13) + chr(10)
        conn.sendall("HTTP/1.1 200 OK" + nl + "Content-Length: 2" + nl + nl + "hi")
    }                                                  # conn closed here, on every path
}                                                      # server closed here

Errors are reported as a negative return (or "" from recv); call last_error() for the message. send uses MSG_NOSIGNAL, so a broken pipe never raises a signal. Only recv and last_error allocate (their returned string).

Throughput tuning (default on every connected socket)

Every socket returned by connect/tcp_connect and accept is tuned for bulk transfer with no caller opt-in: TCP_NODELAY (Nagle off), TCP_QUICKACK (prompt ACKs — re-armed after each recv, since Linux clears it), and an enlarged SO_RCVBUF/SO_SNDBUF. Without these a pure download sits in delayed-ACK slow start — one segment per round-trip — which crawls even though the CPU is idle. Guarded so getsockopt proves the options on both ends (socket_test.cpp ConnectedSocketsAreTunedByDefault).

Measured against known-fast references (2026-07-18; scripts/net_bench_compare.sh, scripts/tls_loopback_bench.sh):

  • On a throttled WAN link, a cheatah HTTPS GET reaches 0.90× curl on the identical URL (parity — both pinned by the throttle; the download path itself is not the limiter).

  • Throttle-free over loopback vs a real openssl s_server, cheatah TLS sustains ~210 MB/s (byte-identity verified) — far above any real link. curl hits ~1500 MB/s there; the remaining gap is per-record TLS framing (copies + per-record key re-expansion), not the cipher: cheatah AES-128-GCM benches at 3.56 GiB/s against OpenSSL's 3.41 — a tie, not a win (1.04× is well inside the 1.15× band we require before calling anything faster; see the crypto table, re-measured 2026-08-19 over 9 interleaved repetitions). Closing the framing gap only matters above ~200 MB/s links and is tracked as evidence-gated follow-up.

Secure clients are built on this: the from-scratch tls 1.3 client rides a connected socket, and requests (pure cheatah) and websocket are layered on top. Plain http:// works directly here.

Per-function docs (parameters, runtime complexity, heap behavior) are in socket.hpp. Tested in ../tests/socket_test.cpp (real loopback round-trips); ASan + Valgrind clean via the QA gate (security/run-valgrind.sh).

Classes

Functions

fn long long socket() source#

Create an IPv4 TCP socket.

Allocates an unbound, unconnected AF_INET/SOCK_STREAM fd; you must follow up with bind()+listen() or connect() before it can carry data, and close() it when done.

Returns

the new fd, or -1 on error.

Complexity

O(1).

Allocation

none.

Compile-run testSocketCompileRun.Socket
System testStdlibE2E.Socket
Performancenetwork/syscall-bound — not micro-benchmarked
fn long long set_reuseaddr(long long fd) source#

Enable SO_REUSEADDR on fd.

Lets a subsequent bind() reuse a local address still lingering in TIME_WAIT, so a restarted server can re-listen on the same port immediately; call it before bind().

Warning

SO_REUSEADDR trades TIME_WAIT protection for restartability: by skipping the kernel's cooldown, delayed segments from a previous connection on the same address can in principle reach the new socket.

Parameters
fd

the socket.

Returns

0 on success, -1 on error.

Complexity

O(1).

Allocation

none.

System testStdlibE2E.Socket
Performancenetwork/syscall-bound — not micro-benchmarked
fn long long bind(long long fd, const std::string &host, long long port) source#

Bind fd to host:port.

Resolves host via getaddrinfo and assigns the resulting local address to the socket; a resolution failure returns -1 with errno set to EADDRNOTAVAIL (see the ResolveFailure test).

Parameters
fd

the socket.

host

interface to bind.

port

TCP port (0 = OS-assigned).

Returns

0 on success, -1 on error.

Complexity

O(1) + resolution.

Allocation

none (the resolver's transient getaddrinfo list is freed before returning).

Compile-run testSocketCompileRun.Bind
System testStdlibE2E.Socket
Performancenetwork/syscall-bound — not micro-benchmarked
fn long long listen(long long fd, long long backlog) source#

Mark fd as a passive (listening) socket.

Switches an already-bound socket into the listening state so accept() can pull connections from it; backlog caps how many fully-established connections may queue before new ones are refused.

Parameters
fd

the socket.

backlog

queue length.

Returns

0 on success, -1 on error.

Complexity

O(1).

Allocation

none.

Compile-run testSocketCompileRun.Listen
System testStdlibE2E.Socket
Performancenetwork/syscall-bound — not micro-benchmarked
fn long long connect(long long fd, const std::string &host, long long port) source#

Connect fd to host:port.

Resolves host and blocks until the TCP handshake succeeds or fails; a refused connection returns -1 with errno ECONNREFUSED (see the ConnectRefused test). Unlike tcp_connect() it does not close the fd on failure — the caller still owns fd.

Parameters
fd

the socket.

host

destination.

port

destination port.

Returns

0 on success, -1 on error.

Complexity

O(1) + DNS.

Allocation

none (the resolver's transient getaddrinfo list is freed before returning).

Concurrency

blocks until the TCP handshake completes or fails.

Compile-run testSocketCompileRun.Connect
System testStdlibE2E.Socket
Performancenetwork/syscall-bound — not micro-benchmarked
fn long long accept(long long fd) source#

Accept one pending connection.

Blocks until a client connects, then returns a new fd for that one connection (the listening fd stays open for further accepts). The returned client fd is owned by the caller and must be closed separately; the peer address is discarded.

Parameters
fd

a listening fd.

Returns

the connected client fd, or -1 on error.

Complexity

O(1) syscall (blocks until a client arrives).

Allocation

none.

Concurrency

blocks the calling thread until a client connects.

Compile-run testSocketCompileRun.Accept
Performancenetwork/syscall-bound — not micro-benchmarked
fn long long local_port(long long fd) source#

The local TCP port fd is bound to (useful after binding to port 0).

Reads the address actually assigned via getsockname and returns its port in host byte order; this is the way to discover the ephemeral port the OS chose when you bound to port 0.

Parameters
fd

a bound socket.

Returns

the port, or -1 on error.

Complexity

O(1) syscall.

Allocation

none.

Performancenetwork/syscall-bound — not micro-benchmarked
fn long long send(long long fd, const std::string &data) source#

Send some of data (one send).

Issues a single send, which may transmit fewer bytes than supplied (a partial send); the caller is responsible for re-sending the remainder, or use sendall() to loop automatically.

Parameters
fd

a connected fd.

data

bytes to send.

Returns

bytes actually sent, or -1 on error.

Complexity

O(n).

Allocation

none (MSG_NOSIGNAL, so a broken pipe never raises SIGPIPE).

Compile-run testSocketCompileRun.Send
System testStdlibE2E.Socket
Performancenetwork/syscall-bound — not micro-benchmarked
fn long long sendall(long long fd, const std::string &data) source#

Send data in full, looping until all bytes are written.

Repeatedly calls send on the unsent remainder until every byte is written, so unlike send() there are no partial sends to handle; it aborts with -1 the moment a send returns <= 0 (error or peer hang-up), in which case some bytes may already have been transmitted.

Parameters
fd

a connected fd.

data

bytes to send.

Returns

0 on success, -1 on error.

Complexity

O(n).

Allocation

none.

Concurrency

may block while the peer's receive window is full; bounded per send by the set_timeout() send deadline.

Compile-run testSocketCompileRun.Sendall
Performancenetwork/syscall-bound — not micro-benchmarked
fn std::string recv(long long fd, long long bufsize) source#

Receive up to bufsize bytes.

Blocks for one recv and returns whatever bytes arrive (possibly fewer than bufsize); the result is binary-safe, so a returned string may contain embedded NULs and its length is the true byte count. A clean EOF (peer closed) and an error both yield "", so check last_error() to tell them apart; bufsize <= 0 also returns "" without touching the socket.

Parameters
fd

a connected fd.

bufsize

maximum bytes to read.

Returns

the bytes read (binary-safe), or "" on EOF/error.

Complexity

O(bufsize).

Allocation

allocates the returned string (and grows a reused per-thread scratch buffer up to bufsize on first use).

Concurrency

blocks until data, EOF, or the set_timeout() deadline; a shutdown() from another thread wakes it with EOF.

Compile-run testSocketCompileRun.Recv
Performancenetwork/syscall-bound — not micro-benchmarked
fn long long close(long long fd) source#

Close a socket.

Releases the fd back to the OS; after this the fd is invalid and must not be reused. Closing an already-closed or never-opened fd fails with -1 (EBADF), which is how the BadFd test exercises the error path.

Parameters
fd

the fd to close.

Returns

0 on success, -1 on error.

Complexity

O(1) syscall.

Allocation

none.

Compile-run testSocketCompileRun.Close
Performancenetwork/syscall-bound — not micro-benchmarked
fn long long shutdown(long long fd) source#

Half-close fd in both directions (::shutdown SHUT_RDWR) WITHOUT releasing it.

A blocking recv() on another thread returns immediately (EOF) — the safe way to wake a reader for a clean shutdown. The fd is still owned by the caller and must be close()d afterwards.

Parameters
fd

the fd to half-close.

Returns

0 on success, -1 on error.

Complexity

O(1) syscall.

Allocation

none.

Concurrency

safe to call from another thread while a recv() on fd blocks — waking that reader is exactly what it is for.

fn long long tcp_listen(const std::string &host, long long port, long long backlog) source#

Create a TCP socket bound to host:port and put it in the listening state (sets SO_REUSEADDR).

Performs socket()/set_reuseaddr/bind/listen in one shot; on any failure it closes the partially-created socket and returns -1, so the caller never leaks an fd. On success the returned fd is owned by the caller and must be passed to close() when done.

Parameters
host

interface to bind ("127.0.0.1", "0.0.0.0", …).

port

TCP port (0 = let the OS pick — read it back with local_port()).

backlog

pending-connection queue length.

Returns

the listening fd, or -1 on error.

Complexity

O(1) (a few syscalls).

Allocation

none (the resolver's transient getaddrinfo list is freed before returning).

Performancenetwork/syscall-bound — not micro-benchmarked
fn long long tcp_connect(const std::string &host, long long port) source#

Create a TCP socket and connect it to host:port.

The host is resolved via getaddrinfo, so names, "localhost", and dotted IPs all work; the connect blocks until the handshake completes or fails. On failure the socket is closed and -1 is returned; on success the caller owns the connected fd and must close() it.

Parameters
host

destination host (name or IP).

port

destination port.

Returns

the connected fd, or -1 on error.

Complexity

O(1) + DNS resolution.

Allocation

none (the resolver's transient getaddrinfo list is freed before returning).

Concurrency

blocks until the TCP handshake completes or fails.

Performancenetwork/syscall-bound — not micro-benchmarked
fn long long set_timeout(long long fd, long long timeout_ms) source#

Bound both blocking directions of fd by timeout_ms (SO_RCVTIMEO + SO_SNDTIMEO), so a silent peer cannot hang a recv/send forever.

A recv that times out returns "" (check last_error() to distinguish from EOF). timeout_ms <= 0 clears the timeouts (block forever).

Parameters
fd

a socket.

timeout_ms

the per-operation bound in milliseconds.

Returns

0 on success, -1 on error.

Complexity

O(1) (two setsockopt calls).

Allocation

none.

fn std::string last_error() source#

The message for the current errno.

Returns the human-readable text for the thread's current errno; call it right after a function reports failure (a -1 return, or "" from recv), since any later syscall may overwrite errno.

Returns

strerror(errno).

Complexity

O(1).

Allocation

allocates the returned string.

System testStdlibE2E.Socket
Performancenetwork/syscall-bound — not micro-benchmarked
fn Conn open(const std::string &host, long long port) source#

Open a client TCP connection to host:port and return it as an owning Conn (the RAII, with-friendly form of tcp_connect()).

Parameters
host

destination host (name or IP).

port

destination port.

Returns

an owning Conn; on failure its is_open() is false (see last_error()).

Complexity

O(1) + DNS resolution.

Allocation

none beyond the Conn itself.

Concurrency

blocks until the TCP handshake completes or fails.

fn Listener serve(const std::string &host, long long port, long long backlog) source#

Create a listening server socket bound to host:port and return it as an owning Listener (the RAII, with-friendly form of tcp_listen()).

Parameters
host

interface to bind ("127.0.0.1", "0.0.0.0", …).

port

TCP port (0 = let the OS pick — read it back with Listener::local_port()).

backlog

pending-connection queue length.

Returns

an owning Listener; on failure its is_open() is false (see last_error()).

Complexity

O(1) (a few syscalls).

Allocation

none beyond the Listener itself.