Previous: Server Example, Up: Connections

#### 16.9.8 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` (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;
               }
             }
         }