UNIX: разработка сетевых приложений
23 n = Recv(connfd, buff, sizeof(buff) - 1, MSG_OOB);24 buff[n] = 0; /* завершающий нуль */25 printf("read %d OOB byte: %s\n", n, buff);26 justreadoob = 1;27 FD_CLR(connfd, &xset);28 }29 if (FD_ISSET(connfd, &rset)) {30 if ((n = Read(connfd, buff, sizeof(buff) - 1)) == 0) {31 printf("received EOF\n");32 exit(0);33 }34 buff[n] = 0; /* завершающий нуль */35 printf("read %d bytes: %s\n", n, buff);36 justreadoob = 0;37 }38 }39 }Мы объявляем новую переменную с именем5, которая указывает, какие данные мы считываем — внеполосные или обычные. Этот флаг определяет, нужно ли вызывать функциюjustreadoobдля проверки на наличие исключительной ситуации.selectКогда мы устанавливаем флаг26-27, мы также должны выключить бит соответствующего дескриптора в наборе для проверки исключительных ситуаций.justreadoobТеперь программа работает так, как мы ожидали.
24.3. Функция sockatmark
С приемом внеполосных данных всегда связана так называемая отметка внеполосных данных (out-of-bandmark). Это позиция в потоке обычных данных на стороне отправителя, соответствующая тому моменту; когда посылающий процесс отправляет байт, содержащий внеполосные данные. Считывая данные из сокета, принимающий процесс путем вызова функции
определяет, находится ли он в данный момент на этой отметке.sockatmark#include <sys/socket.h>int sockatmark(int <i>sockfd</i>);<i>Возвращает: 1, если находится на отметке внеполосных данных: 0, если не на отметке; -1 в случае ошибки</i>ПРИМЕЧАНИЕЭта функция появилась в POSIX. Разработчики стандарта POSIX стремятся заменить отдельными функциями все вызовы ioctl с различными параметрами.
В листинге 24.5 показана реализация этой функции с помощью поддерживаемого в большинстве систем параметра
функцииSIOCATMARK.ioctlЛистинг 24.5. Функция sockatmark реализована с использованием функции ioctl
//lib/sockatmark.c1 #include "unp.h"2 int3 sockatmark(int fd)4 {5 int flag;6 if (ioctl(fd, SIOCATMARK, &flag) < 0)7 return (-1);8 return (flag != 0 ? 1 : 0);9 }Отметка внеполосных данных применима независимо от того, как принимающий процесс получает внеполосные данные: вместе с обычными данными (параметр сокета
) или отдельно (флагSO_OOBINLINE). Отметка внеполосных данных часто используется для того, чтобы принимающий процесс мог интерпретировать получаемые данные специальным образом до тех пор, пока он не дойдет до этой отметки.MSG_OOBПример: особенности отметки внеполосных данных
Далее мы приводим простой пример, иллюстрирующий следующие две особенности отметки внеполосных данных:
1. Отметка внеполосных данных всегда указывает на один байт дальше конечного байта обычных данных. Это означает, что, когда внеполосные данные получены вместе с обычными, функция
возвращает 1, если следующий считываемый байт был послан с флагомsockatmark. Если параметрMSG_OOBне включен (состояние по умолчанию), то функцияSO_OOBINLINEвозвращает 1, когда следующий байт данных является первым байтом, посланным следом за внеполосными данными.sockatmark2. Операция считывания всегда останавливается на отметке внеполосных данных [128, с. 519–520]. Это означает, что если в приемном буфере сокета 100 байт, но только 5 из них расположены перед отметкой внеполосных данных, то когда процесс выполнит функцию
, запрашивая 100 байт, возвратятся только 5 байт, расположенные до этой отметки. Эта вынужденная остановка на отметке позволяет процессу вызвать функциюread, которая определит, находится ли указатель буфера на отметке внеполосных данных.sockatmarkВ листинге 24.6 показана наша программа отправки. Она посылает три байта обычных данных, один байт внеполосных данных, а затем еще один байт обычных данных. Паузы между этими операциями отсутствуют.
В листинге 24.7 показана принимающая программа. В ней не используется ни функция
, ни сигналselect. Вместо этого в ней вызывается функцияSIGURG, определяющая положение байта внеполосных данных.sokatmarkЛистинг 24.6. Программа отправки
//oob/tcpsen04.c1 #include "unp.h"2 int3 main(int argc, char **argv)4 {5 int sockfd;6 if (argc != 3)7 err_quit("usage: tcpsend04 <host> <port#>");8 sockfd = Tcp_connect(argv[1], argv[2]);9 Write(sockfd, "123", 3);10 printf("wrote 3 bytes of normal data\n");11 Send(sockfd, "4", 1, MSG_OOB);12 printf("wrote 1 byte of OOB data\n");