Stream output from a C program

Call setbuf(stdout, NULL) as the first statement of main, before any output. That turns off the C standard library's buffering on stdout for the rest of the process, so every printf goes straight down the pipe.

cc -o count count.c
websocketd --port=8080 ./count
#include <stdio.h>
#include <unistd.h>

int main(void) {
    setbuf(stdout, NULL);

    for (int i = 1; i <= 5; i++) {
        printf("%d\n", i);
        usleep(500000);
    }

    return 0;
}

Connect a client and the five numbers arrive half a second apart. Remove the setbuf line and all five arrive together when the program exits. Output buffering explains why.

Call it before you write anything. setbuf must be applied to a stream that has had no input or output performed on it yet, so the top of main is the place for it.

setvbuf, if you prefer the explicit form

setvbuf does the same job with the mode spelled out, and returns a value you can check:

setvbuf(stdout, NULL, _IONBF, 0);

_IONBF means unbuffered. The two other modes name the behaviour this page is working around: _IOLBF is line-buffered, which flushes on each newline, and _IOFBF is fully buffered, which is what your program gets by default when stdout is a pipe.

_IOLBF is a reasonable middle choice under websocketd, since a WebSocket message boundary is a newline anyway:

setvbuf(stdout, NULL, _IOLBF, 0);

fflush, if you want to keep the buffer

Where the program produces a lot of output and you want to keep buffering for the bulk of it, leave the stream alone and call fflush(stdout) after the writes that must go out now:

for (int i = 1; i <= 5; i++) {
    printf("%d\n", i);
    fflush(stdout);
    usleep(500000);
}

This streams identically. The cost is one call to remember at every site that matters. Forget one and that message silently never arrives.

For a program you cannot recompile

If the binary is not yours, wrap it in stdbuf, which sets the buffering mode through the loader before the program starts:

websocketd --port=8080 stdbuf -oL ./legacy-binary

-oL makes stdout line-buffered. This works only for programs that use the C standard library's stdio and have not set their own buffering explicitly. stdbuf ships with GNU coreutils, so it is present on typical Linux systems and not on macOS by default.

Next