Листинг 24.1. Простая программа отправки внеполосных данных
//oob/tcpsend01.c
1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4 {
5 int sockfd;
6 if (argc != 3)
7 err_quit("usage: tcpsend01 <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 sleep(1);
12 Send(sockfd, "4", 1, MSG_OOB);
13 printf("wrote 1 byte of OOB data\n");
14 sleep(1);
15 Write(sockfd, "56", 2);
16 printf("wrote 2 bytes of normal data\n");
17 sleep(1);
18 Send(sockfd, "7", 1, MSG_OOB);
19 printf("wrote 1 byte of OOB data\n");
20 sleep(1);
21 Write(sockfd, "89", 2);
22 printf("wrote 2 bytes of normal data\n");
23 sleep(1);
24 exit(0);
25 }
Отправлены 9
байт, промежуток между операциями по отправке установлен с помощью функции
sleep
равным одной секунде. Назначение этого промежутка в том, чтобы данные каждой из функций
write
или
send
были переданы и получены на другом конце как отдельный сегмент TCP. Несколько позже мы обсудим некоторые вопросы согласования во времени при пересылке внеполосных данных. После выполнения данной программы мы видим вполне предсказуемый результат:
macosx % tcpsend01 freebsd 9999
wrote 3 bytes of normal data
wrote 1 byte of OOB data
wrote 2 bytes of normal data
wrote 1 byte of OOB data
wrote 2 bytes of normal data
В листинге 24.2 показана принимающая программа.
Листинг 24.2. Простая программа для получения внеполосных данных
33 n = Recv(connfd, buff, sizeof(buff) - 1, MSG_OOB);
34 buff[n] = 0; /* завершающий нуль */
35 printf("read OOB byte: %s\n", n, buff);
36 }
Установка обработчика сигнала и владельца сокета
16-17
Устанавливается обработчик сигнала
SIGURG
и функция
fcntl
задает владельца сокета для данного соединения.
ПРИМЕЧАНИЕ
Обратите внимание, что мы не задаем обработчик сигнала, пока не завершается функция accept. Существует небольшая вероятность того, что внеполосные данные могут прибыть после того, как TCP завершит трехэтапное рукопожатие, но до завершения функции accept. Внеполосные данные мы в этом случае потеряем. Допустим, что мы установили обработчик сигнала перед вызовом функции accept, а также задали владельца прослушиваемого сокета (который затем стал бы владельцем присоединенного сокета). Тогда, если внеполосные данные прибудут до завершения функции accept, наш обработчик сигналов еще не получит значения для дескриптора connfd. Если данный сценарий важен для приложения, следует инициализировать connfd, «вручную» присвоив этому дескриптору значение -1, добавить в обработчик проверку равенства connfd ==-1 и при истинности этого условия просто установить флаг, который будет проверяться в главном цикле после вызова accept. За счет этого главный цикл сможет узнать о поступлении внеполосных данных и считать их. Можно заблокировать сигнал на время вызова accept, но при этом программа будет страдать от всех возможных ситуаций гонок, описанных в разделе 20.5.