Out-of-Band Data
----------------
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'
(Note: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 (Note: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 Note:Interrupt Input. You must also establish a
handler for this signal, as described in Note: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. Note: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;
}
}
}