UNIX: разработка сетевых приложений
19 exit(0);20 }Этот процесс устанавливает размер буфера отправки сокета равным 32 768 байт, записывает 16 384 байт обычных данных, а затем на 5 с переходит в спящее состояние. Чуть ниже мы увидим, что приемник устанавливает размер приемного буфера сокета равным 4096 байт, поэтому данные, отправленные отсылающим TCP, с гарантией заполнят приемный буфер сокета получателя. Затем отправитель посылает один байт внеполосных данных, за которым следуют 1024 байт обычных данных, и, наконец, закрывает соединение.9-19В листинге 24.9 представлена принимающая программа.
Листинг 24.9. Принимающая программа
//oob/tcprecv05.c1 #include "unp.h"2 int listenfd, connfd;3 void sig_urg(int);4 int5 main(int argc, char **argv)6 {7 int size;8 if (argc == 2)9 listenfd = Tcp_listen(NULL, argv[1], NULL);10 else if (argc == 3)11 listenfd = Tcp_listen(argv[1], argv[2], NULL);12 else13 err_quit("usage: tcprecv05 [ <host> ] <port#>");14 size = 4096;15 Setsockopt(listenfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));16 connfd = Accept(listenfd, NULL, NULL);17 Signal(SIGURG, sig_urg);18 Fcntl(connfd, F_SETOWN, getpid());19 for (;;)20 pause();21 }22 void23 sig_urg(int signo)24 {25 int n;26 char buff[2048];27 printf("SIGURG received\n");28 n = Recv(connfd, buff, sizeof(buff) - 1, MSG_OOB);29 buff[n] = 0; /* завершающий пустой байт */30 printf("read %d OOB byte\n", n);31 }Принимающий процесс устанавливает размер приемного буфера сокета приемника равным 4096 байт. Этот размер наследуется присоединенным сокетом после установления соединения. Затем процесс вызывает функцию14-20, задает обработчик для сигналаacceptи задает владельца сокета. В главном цикле (бесконечном) вызывается функцияSIGURG.pauseОбработчик сигнала вызывает функцию22-31для считывания внеполосных данных.recvЕсли мы запускаем сначала принимающую программу, а затем программу отправки, то получаем следующий результат выполнения программы отправки:
macosx % <b>tcpsend05 freebsd 5555</b>wrote 16384 bytes of normal datawrote 1 byte of OOB datawrote 1024 bytes of normal dataКак и ожидалось, все данные помещаются в буфер отправки сокета отправителя, и программа завершается. Ниже приведен результат работы принимающей программы:
freebsd4 % <b>tcprecv05 5555</b>SIGURG receivedrecv error: Resource temporarily unavailableСообщение об ошибке, которое выдает наша функция
, соответствует ошибкеerr_sys, которая в FreeBSD аналогична ошибкеEAGAIN. TCP посылает уведомление об отправке внеполосных данных принимающему TCP, который в результате генерирует сигналEWOULDBLOCKдля принимающего процесса. Но когда вызывается функцияSIGURGи задается флагrecv, байт с внеполосными данными не может быть прочитан.MSG_OOBДля решения этой проблемы необходимо, чтобы получатель освобождал место в своем приемном буфере, считывая поступившие обычные данные. В результате TCP объявит для отправителя окно ненулевого размера, что в конечном счете позволит отправителю передать байт, содержащий внеполосные данные.
ПРИМЕЧАНИЕВ реализациях, происходящих от Беркли [128, с. 1016-1017], можно отметить две близких проблемы. Во-первых, даже если приемный буфер сокета заполнен, ядро всегда принимает от процесса внеполосные данные для отправки собеседнику. Во-вторых, когда отправитель посылает байт с внеполосными данными, немедленно посылается сегмент TCP, содержащий срочное уведомление. Все обычные проверки вывода TCP (алгоритм Нагла, предотвращение синдрома «глупого окна») при этом блокируются.
Пример: единственность отметки внеполосных данных в TCP
Нашим очередным примером мы иллюстрируем тот факт, что для данного соединения TCP существует всего одна отметка внеполосных данных, и если новые внеполосные данные прибудут прежде, чем принимающий процесс начнет считывать пришедшие ранее внеполосные данные, то предыдущая отметка будет утеряна.
В листинге 24.10 показана посылающая программа, аналогичная программе, приведенной в листинге 24.6. Отличие заключается в том, что сейчас мы добавили еще одну функцию
для отправки внеполосных данных и еще одну функциюsendдля записи обычных данных.writeЛистинг 24.10. Отправка двух байтов внеполосных данных друг за другом
//oob/tcpsend06.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#>");