Overview
Imagine you are downloading a large file using FTP and you want to cancel it immediately. The cancel command needs to “jump the queue” and reach the server before the download data that is already in the pipe. This is the problem that out-of-band (OOB) data was designed to solve — sending high-priority data that the receiver can detect and process without reading all the normal data first.
However, OOB data in TCP has a severe limitation and is now discouraged. This tutorial explains what it is, how it works, why it is limited, and what to use instead.
Key Terms
1. What is Out-of-Band Data?
Out-of-band data is a feature of stream sockets (not datagram sockets). It allows a sender to mark certain data as high priority. The receiver is notified that high-priority data is waiting even if it has not yet read all the normal (in-band) data that came before it.
The receiver gets a SIGURG signal when out-of-band data arrives. It can then handle the urgent situation before going back to read the normal data.
Real-world users of OOB data:
- telnet — sends interrupt (Ctrl+C) as OOB to abort a running command
- rlogin — remote login, uses OOB for session control signals
- ftp — uses OOB to abort a running file transfer
2. How to Send and Receive OOB Data
OOB data is sent using send() with the MSG_OOB flag, and received using recv() with the same MSG_OOB flag.
Sender Side
#include <sys/socket.h>
#include <string.h>
/* Send normal data first */
send(sockfd, "Hello World", 11, 0);
/* Send 1 byte of OOB data — must be exactly 1 byte with TCP */
send(sockfd, "!", 1, MSG_OOB);
/* Send more normal data */
send(sockfd, "Goodbye", 7, 0);
Receiver Side — Signal Handler Approach
#include <sys/socket.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>
int oob_socket; /* global so signal handler can use it */
/* SIGURG signal handler — called when OOB data arrives */
void sigurg_handler(int sig)
{
char oob_byte;
int n;
/*
* Use MSG_OOB flag to read the out-of-band byte.
* This reads the OOB data without consuming the normal stream.
*/
n = recv(oob_socket, &oob_byte, 1, MSG_OOB);
if (n == 1) {
printf("Received OOB byte: 0x%02x ('%c')\n",
(unsigned char)oob_byte, oob_byte);
/* Handle the urgent command here */
}
}
int setup_oob_receiver(int sockfd)
{
oob_socket = sockfd;
/* Register the SIGURG handler */
signal(SIGURG, sigurg_handler);
/*
* Tell the kernel which process should receive SIGURG
* when OOB data arrives on this socket.
* Without this, SIGURG is not sent to anyone.
*/
if (fcntl(sockfd, F_SETOWN, getpid()) == -1) {
perror("fcntl F_SETOWN");
return -1;
}
return 0;
}
int main(void)
{
int sockfd;
char buf[256];
ssize_t n;
/* ... connect/accept ... */
setup_oob_receiver(sockfd);
/* Normal read loop — SIGURG will interrupt this asynchronously */
while ((n = read(sockfd, buf, sizeof(buf))) > 0) {
buf[n] = '\0';
printf("Normal data: %s\n", buf);
}
return 0;
}
fcntl(sockfd, F_SETOWN, getpid()) to register your process as the owner of the socket. Without this, the kernel does not know where to send SIGURG and the signal is simply discarded.3. TCP’s Critical Limitation: Only 1 Byte at a Time
This is the most important thing to know about OOB data in TCP: at most 1 byte can be marked as out-of-band at any one time.
This is not an artificial API restriction — it comes from how TCP itself works internally.
How TCP Urgent Mode Works Internally
The Overwrite Problem
/* PROBLEM: What happens if sender sends two OOB bytes quickly */
send(sockfd, "A", 1, MSG_OOB); /* First OOB byte */
send(sockfd, "B", 1, MSG_OOB); /* Second OOB byte */
/*
* If the receiver has not yet read the first OOB byte "A",
* the indication for "A" is LOST when "B" arrives.
* TCP only keeps track of ONE urgent position at a time.
*
* The receiver will only see "B" as OOB data.
* "A" becomes indistinguishable from normal stream data.
*/
4. UNIX Domain Sockets and OOB Data
Some UNIX implementations support out-of-band data for UNIX domain stream sockets as well. Linux does not support this. OOB data on Linux only works with TCP (Internet domain stream sockets).
MSG_OOB on a UNIX domain socket on Linux will fail with EOPNOTSUPP (operation not supported).5. Why OOB Data Is Discouraged Today
Modern socket programming advice discourages the use of out-of-band data for several reasons:
- Limited to 1 byte at a time in TCP
- Can be unreliable in some network configurations and TCP implementations
- Behavior is not consistent across all UNIX implementations
- Some network equipment (firewalls, routers) may strip or mishandle urgent data
- Only works with stream sockets
Better Alternative: Two Separate Socket Connections
The recommended modern approach is to maintain two separate stream sockets between the same pair of communicating processes:
fd = data_sock
fd = ctrl_sock
read/write here
monitored with select()
select() or poll() to monitor both sockets simultaneouslyAdvantages of two sockets over OOB data:
- Can send multiple bytes of priority data (not limited to 1 byte)
- Works with any stream socket domain (UNIX domain, TCP, etc.)
- Much more portable and reliable
- Easier to reason about and debug
#include <sys/socket.h>
#include <sys/select.h>
/*
* Modern approach: two separate connections.
* Monitor both with select() in a single loop.
*/
void two_socket_server(int data_fd, int ctrl_fd)
{
fd_set readfds;
char buf[1024];
int maxfd = (data_fd > ctrl_fd) ? data_fd : ctrl_fd;
ssize_t n;
for (;;) {
FD_ZERO(&readfds);
FD_SET(data_fd, &readfds);
FD_SET(ctrl_fd, &readfds);
if (select(maxfd + 1, &readfds, NULL, NULL, NULL) < 0) {
perror("select");
break;
}
/* Check high-priority control channel first */
if (FD_ISSET(ctrl_fd, &readfds)) {
n = read(ctrl_fd, buf, sizeof(buf));
if (n > 0) {
buf[n] = '\0';
printf("PRIORITY command: %s\n", buf);
/* e.g., "ABORT", "PAUSE", "CANCEL" */
if (strncmp(buf, "ABORT", 5) == 0) break;
}
}
/* Then handle normal data */
if (FD_ISSET(data_fd, &readfds)) {
n = read(data_fd, buf, sizeof(buf));
if (n > 0) {
printf("Normal data: %zu bytes\n", (size_t)n);
/* Process the data */
}
}
}
}
Interview Questions
Out-of-band data is a feature of stream sockets that allows a sender to mark certain data as high-priority. The receiver is notified of this high-priority data (via the SIGURG signal) without needing to first read all the normal data in the stream. It solves the problem of “jumping the queue” — for example, FTP sending an abort command while a large file is being transferred.
send(sockfd, data, 1, MSG_OOB) is used to send OOB data. recv(sockfd, &byte, 1, MSG_OOB) is used in the signal handler to read the OOB byte. The MSG_OOB flag is the key difference from normal I/O calls.
SIGURG is a signal sent by the kernel to the socket owner process when out-of-band data arrives on a socket. The application must first call fcntl(sockfd, F_SETOWN, getpid()) to register itself as the socket owner; otherwise, SIGURG is not delivered. The signal handler then calls recv() with MSG_OOB to read the urgent byte.
TCP marks urgent data using the URG bit in the TCP header and an urgent pointer field that points to the location of the urgent byte in the segment. However, TCP provides no way to indicate the length of the urgent data — only its end position. This mismatch between the generic sockets API model and TCP’s specific implementation means urgent data is effectively limited to 1 byte. If a second OOB byte arrives before the first is read, the indication for the first byte is lost.
No. Some other UNIX implementations support OOB data for UNIX domain stream sockets, but Linux does not. On Linux, OOB data (MSG_OOB) is only supported for TCP (Internet domain stream) sockets. Using MSG_OOB on a UNIX domain socket on Linux will return an error.
The recommended alternative is to maintain two separate stream socket connections between the same pair of processes: one for normal data and one for high-priority control messages. The server monitors both using select() or poll() and checks the control channel first. This approach is more reliable, supports multiple bytes of priority data, works with any socket domain, and is portable across UNIX implementations.
telnet, rlogin, and ftp use OOB data. Telnet uses it to send the interrupt character (Ctrl+C) to the remote end, allowing the user to abort a running program even when data is buffered in the stream. FTP uses it to send abort and interrupt commands during file transfers.
