Previous: Server Example, Up: Connections
Streams with connections permit out-of-band data that is
delivered with higher priority than ordinary data. Typically the
reason for sending out-of-band data is to send notice of an
exceptional condition. To send out-of-band data use
send
, specifying the flag MSG_OOB
(see Sending Data).
Out-of-band data are received with higher priority because the
receiving process need not read it in sequence; to read the next
available out-of-band data, use recv
with the MSG_OOB
flag (see Receiving Data). Ordinary read operations do not read
out-of-band data; they read only ordinary data.
When a socket finds that out-of-band data are on their way, it sends a
SIGURG
signal to the owner process or process group of the
socket. You can specify the owner using the F_SETOWN
command
to the fcntl
function; see Interrupt Input. You must
also establish a handler for this signal, as described in Signal Handling, in order to take appropriate action such as reading the
out-of-band data.
Alternatively, you can test for pending out-of-band data, or wait
until there is out-of-band data, using the select
function; it
can wait for an exceptional condition on the socket. See Waiting for I/O, for more information about select
.
Notification of out-of-band data (whether with SIGURG
or with
select
) indicates that out-of-band data are on the way; the data
may not actually arrive until later. If you try to read the
out-of-band data before it arrives, recv
fails with an
EWOULDBLOCK
error.
Sending out-of-band data automatically places a “mark” in the stream of ordinary data, showing where in the sequence the out-of-band data “would have been”. This is useful when the meaning of out-of-band data is “cancel everything sent so far”. Here is how you can test, in the receiving process, whether any ordinary data was sent before the mark:
success = ioctl (socket, SIOCATMARK, &atmark);
The integer
variable atmark is set to a nonzero value if
the socket's read pointer has reached the “mark”.
Here's a function to discard any ordinary data preceding the out-of-band mark:
int discard_until_mark (int socket) { while (1) { /* This is not an arbitrary limit; any size will do. */ char buffer[1024]; int atmark, success; /* If we have reached the mark, return. */ success = ioctl (socket, SIOCATMARK, &atmark); if (success < 0) perror ("ioctl"); if (result) return; /* Otherwise, read a bunch of ordinary data and discard it. This is guaranteed not to read past the mark if it starts before the mark. */ success = read (socket, buffer, sizeof buffer); if (success < 0) perror ("read"); } }
If you don't want to discard the ordinary data preceding the mark, you
may need to read some of it anyway, to make room in internal system
buffers for the out-of-band data. If you try to read out-of-band data
and get an EWOULDBLOCK
error, try reading some ordinary data
(saving it so that you can use it when you want it) and see if that
makes room. Here is an example:
struct buffer { char *buf; int size; struct buffer *next; }; /* Read the out-of-band data from SOCKET and return it as a `struct buffer', which records the address of the data and its size. It may be necessary to read some ordinary data in order to make room for the out-of-band data. If so, the ordinary data are saved as a chain of buffers found in the `next' field of the value. */ struct buffer * read_oob (int socket) { struct buffer *tail = 0; struct buffer *list = 0; while (1) { /* This is an arbitrary limit. Does anyone know how to do this without a limit? */ #define BUF_SZ 1024 char *buf = (char *) xmalloc (BUF_SZ); int success; int atmark; /* Try again to read the out-of-band data. */ success = recv (socket, buf, BUF_SZ, MSG_OOB); if (success >= 0) { /* We got it, so return it. */ struct buffer *link = (struct buffer *) xmalloc (sizeof (struct buffer)); link->buf = buf; link->size = success; link->next = list; return link; } /* If we fail, see if we are at the mark. */ success = ioctl (socket, SIOCATMARK, &atmark); if (success < 0) perror ("ioctl"); if (atmark) { /* At the mark; skipping past more ordinary data cannot help. So just wait a while. */ sleep (1); continue; } /* Otherwise, read a bunch of ordinary data and save it. This is guaranteed not to read past the mark if it starts before the mark. */ success = read (socket, buf, BUF_SZ); if (success < 0) perror ("read"); /* Save this data in the buffer list. */ { struct buffer *link = (struct buffer *) xmalloc (sizeof (struct buffer)); link->buf = buf; link->size = success; /* Add the new link to the end of the list. */ if (tail) tail->next = link; else list = link; tail = link; } } }