UNIX: разработка сетевых приложений
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были переданы и получены на другом конце как отдельный сегмент TCP. Несколько позже мы обсудим некоторые вопросы согласования во времени при пересылке внеполосных данных. После выполнения данной программы мы видим вполне предсказуемый результат:sendmacosx % <b>tcpsend01 freebsd 9999</b>wrote 3 bytes of normal datawrote 1 byte of OOB datawrote 2 bytes of normal datawrote 1 byte of OOB datawrote 2 bytes of normal dataВ листинге 24.2 показана принимающая программа.
Листинг 24.2. Простая программа для получения внеполосных данных
//oob/tcprecv01.c1 #include "unp.h"2 int listenfd, connfd;3 void sig_urg(int);4 int5 main(int argc, char **argv)6 {7 int n;8 char buff[100];9 if (argc == 2)10 listenfd = Tcp_listen(NULL, argv[1], NULL);11 else if (argc == 3)12 listenfd = Tcp_listen(argv[1], argv[2], NULL);13 else14 err_quit("usage: tcprecv01 [ <host> ] <port#>");15 connfd = Accept(listenfd, NULL, NULL);16 Signal(SIGURG, sig_urg);17 Fcntl(connfd, F_SETOWN, getpid());18 for (;;) {19 if ((n = Read(connfd, buff, sizeof(buff) - 1)) == 0) {20 printf("received EOF\n");21 exit(0);22 }23 buff[n] = 0; /* завершающий нуль */24 printf("read bytes: %s\n", n, buff);25 }26 }27 void28 sig_urg(int signo)29 {30 int n;31 char buff[100];32 printf("SIGURG received\n");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.
Процесс считывает данные из сокета и выводит каждую строку, которая возвращается функцией18-25. После того как отправитель разрывает соединение, то же самое делает и получатель.readОбработчик сигнала SIGURGНаш обработчик сигнала вызывает функцию27-36, считывает внеполосные данные, устанавливая флагprintf, а затем выводит полученные данные. Обратите внимание, что при вызове функции recv мы запрашиваем до 100 байт, но, как мы вскоре увидим, всегда возвращается только один байт внеполосных данных.MSG_OOBПРИМЕЧАНИЕКак сказано ранее, вызов ненадежной функции printf из обработчика сигнала не рекомендуется. Мы делаем это просто для того, чтобы увидеть, что произойдет с нашей программой.
Ниже приведен результат, который получается, когда мы запускаем эту программу, а затем — программу для отправки внеполосных данных, приведенную в листинге 24.1.
freebsd % <b>tcprecv01 9999</b>read 3 bytes: 123SIGURG receivedread 1 OOB byte: 4read 2 bytes: 56SIGURG receivedread 1 OOB byte: 7