select exceptfds

2025-09-10 CUNIXipc

And here's what happened

I saw this line in the man page:

But I never saw it actually trigger. So I decided to write a tiny TCP client/server, and try to make select() wake up on the exceptfds set.

TCP’s “out-of-band” data isn’t a second stream or a priority lane. It’s just one single byte flagged as urgent. The sender sets the urgent pointer in the TCP header.

On the receiving end, two options exist:

That’s it. One byte, not a side channel.

pseudocode:

server:
socket()
bind()
listen()

fd_set rfds, efds;
while (true) {
    FD_ZERO(&rfds); FD_ZERO(&efds);
    FD_SET(c, &rfds); FD_SET(c, &efds);

    select(c+1, &rfds, NULL, &efds, NULL);

    if (FD_ISSET(c, &efds)) {
        char b;
        int n = recv(c, &b, 1, MSG_OOB);
        printf("OOB: got '%c'\n", b);
    }
    if (FD_ISSET(c, &rfds)) {
        char buf[64];
        int n = recv(c, buf, sizeof buf, 0);
        if (n <= 0) break;
        printf("INBAND: %.*s\n", n, buf);
    }
}
client:
int s = socket(AF_INET, SOCK_STREAM, 0);
connect(s, ...);

send(s, "hello", 5, 0);
send(s, "!", 1, MSG_OOB);   // the urgent byte
send(s, "world", 5, 0);

Run the server, connect the client, and watch the logs:

INBAND: hello
OOB: got '!'
INBAND: world

Here's the thing:

So the man page was right: exceptfds is just “would recv(MSG_OOB) not block.”

I ran tcpdump -vv while sending. The OOB send set the TCP URG flag and advanced the urgent pointer. You can see it in the packet dump:

Flags [P.], urg 1, seq 6:7, urgptr=1

BSD vs Linux

There’s an old historical quirk: BSD stacks interpret the urgent pointer differently. On FreeBSD/macOS, the “urgent byte” is actually delivered as the last byte of the stream, not a separate one.

why it matters (or doesn’t)

In the old days, telnet used OOB for control signals. Today, hardly anything does. It’s deprecated in RFC 6093.

But as an experiment:

and if you ever see it in production? Chances are you’re debugging legacy telnet, or something’s gone wrong.