A Linux socket with SO_RCVLOWAT set above one byte can have data queued while poll(), select(), or epoll still reports no normal readable readiness. Since Linux 2.6.28, those readiness interfaces respect the configured receive low-water mark.

The option changes the threshold associated with normal receive readiness. It does not define message boundaries, reserve receive-buffer space, or guarantee that a later receive operation returns exactly the configured number of bytes.

Readability can require more than one queued byte

Socket receive readiness is usually observed with the default low-water mark of one byte. In that state, ordinary queued data is enough to satisfy the data-volume part of the readable condition.

SO_RCVLOWAT raises that byte threshold. A value of 4096 means that ordinary receive readiness can remain false while fewer than 4096 bytes are available, then become true when the queue reaches the threshold.

int lowat = 4096;

if (setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT,
               &lowat, sizeof(lowat)) == -1) {
    /* handle error */
}

Linux permits changing SO_RCVLOWAT; the default value is 1. The related SO_SNDLOWAT option is not changeable on Linux.

The threshold affects readiness and blocking receive behavior

The low-water mark matters at two related boundaries. A blocking receive can wait for the receive condition associated with the configured threshold, and modern Linux readiness interfaces use the same setting when deciding whether normal data makes the socket readable.

That coupling prevents a common mismatch: a readiness wait returning for a single queued byte followed by a blocking receive waiting for a larger low-water threshold. Linux kernels before 2.6.28 had that mismatch because select(), poll(), and epoll did not respect SO_RCVLOWAT.

For nonblocking sockets, receive calls still retain nonblocking semantics. If an operation cannot proceed, it returns an error such as EAGAIN or EWOULDBLOCK rather than sleeping. Readiness remains a state indication, not a reservation of bytes for a particular thread.

Readiness does not preserve a byte allocation

A readiness event says that the socket satisfied a readiness condition when the kernel evaluated it. It does not assign the queued bytes to the waiter.

Another thread or process sharing the socket can consume data before the original waiter calls recv(). Protocol state can also introduce other readable conditions, including orderly shutdown or pending errors. Code using shared sockets therefore cannot treat a reported event as ownership of SO_RCVLOWAT bytes.

This distinction is especially important with event loops. The low-water mark can suppress wakeups for small arrivals, but it is not a transaction boundary around the receive queue.

TCP remains a byte stream

On a TCP socket, SO_RCVLOWAT = 4096 does not make 4096-byte records. TCP still exposes an ordered byte stream, and sender write boundaries remain independent from receiver read boundaries.

Once a receive operation runs, its result depends on the requested length, available data, flags, socket mode, protocol state, and applicable receive semantics. The low-water value is not a framing field carried by TCP and is not visible to the peer as an application record size.

A protocol that requires complete application records still needs explicit framing, such as a fixed-size header containing a payload length. SO_RCVLOWAT can alter wakeup behavior around queued bytes, but it cannot replace that framing.

Larger thresholds trade prompt wakeups for batching

Raising the receive low-water mark can reduce normal readable notifications caused by small incremental arrivals. That can be useful when an application naturally processes data in larger batches and can tolerate waiting for the queue to accumulate.

The same mechanism can add latency when traffic arrives slowly. A partial request or response may sit in the receive queue without producing normal data readiness until more bytes arrive or another socket condition changes the result.

The threshold therefore belongs to receive scheduling policy rather than transport framing. Its effect depends on traffic shape, application buffering, concurrency, and the event-loop model.

Kernel version is part of the behavior boundary

Linux has allowed SO_RCVLOWAT to be changed since Linux 2.4. The readiness behavior changed in Linux 2.6.28: from that release onward, select(), poll(), and epoll report normal readable readiness only when at least the configured low-water amount is available.

That version boundary matters for software targeting old kernels or compatibility environments that reproduce older behavior. On current Linux systems, the receive low-water mark is part of the readiness decision itself, not merely a parameter consulted after a receive call begins.