UNIX: разработка сетевых приложений
read 2 bytes: 89received EOFРезультаты оказались такими, как мы и ожидали. Каждый раз, когда отправитель посылает внеполосные данные, для получателя генерируется сигнал
, после чего получатель считывает один байт, содержащий внеполосные данные.SIGURGПростой пример использования функции select
Теперь мы переделаем код нашего получателя внеполосных данных и вместо сигнала
будем использовать функциюSIGURG. В листинге 24.3 показана принимающая программа.selectЛистинг 24.3. Принимающая программа, в которой (ошибочно) используется функция select для уведомления о получении внеполосных данных
//oob/tcprecv02.c1 #include "unp.h"2 int3 main(int argc, char **argv)4 {5 int listenfd, connfd, n;6 char buff[100];7 fd_set rset, xset;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: tcprecv02 [ <host> ] <port#>");14 connfd = Accept(listenfd, NULL, NULL);15 FD_ZERO(&rset);16 FD_ZERO(&xset);17 for (;;) {18 FD_SET(connfd, &rset);19 FD_SET(connfd, &xset);20 Select(connfd + 1, &rset, NULL, &xset, NULL);21 if (FD_ISSET(connfd, &xset)) {22 n = Recv(connfd, buff, sizeof(buff) - 1, MSG_OOB);23 buff[n] =0; /* завершающий нуль */24 printf("read OOB byte: %s\n", n, buff);25 }26 if (FD_ISSET(connfd, &rset)) {27 if ((n = Read(connfd, buff, sizeof(buff) - 1)) == 0) {28 printf("received EOF\n");29 exit(0);30 }31 buff[n] = 0; /* завершающий нуль */32 printf("read bytes: %s\n", n, buff);33 }34 }35 }Процесс вызывает функцию15-20, которая ожидает получения либо обычных данных (набор дескрипторов для чтения,select), либо внеполосных (набор дескрипторов для обработки исключений,rset). В обоих случаях полученные данные выводятся.xsetЕсли мы запустим эту программу, а затем — программу для отправки, которая приведена в листинге 24.1, то столкнемся со следующей ошибкой:
freebsd4 % <b>tcprecv02 9999</b>read 3 bytes: 123read 1 OOB byte: 4recv error: Invalid argumentПроблема заключается в том, что функция
будет сообщать об исключительной ситуации, пока процесс не считает данные, находящиеся за отметкой внеполосных данных (то есть после них [128, с. 530-531]). Мы не можем считывать внеполосные данные больше одного раза, так как после первого же их считывания ядро очищает буфер, содержащий один байт внеполосных данных. Когда мы вызываем функциюselect, устанавливая флагrecvво второй раз, она возвращает ошибкуMSG_OOB.EINVALЧтобы решить эту проблему, нужно вызывать функцию
для проверки на наличие исключительной ситуации только после того, как будут приняты все обычные данные. В листинге 24.4 показана модифицированная версия принимающей программы из листинга 24.3. В этой версии описанный сценарий обрабатывается корректно.selectЛистинг 24.4. Модификация программы, приведенной в листинге 24.3. Функция select применяется для проверки исключительной ситуации корректным образом
//oob/tcprecv03.c1 #include "unp.h"2 int3 main(int argc, char **argv)4 {5 int listenfd, connfd, n, justreadoob = 0;6 char buff[100];7 fd_set rset, xset;8 if (argc == 2)9 listenfd = Tcp_listen(NULL, argv[1], NULL);10 else if (argc == 3)11 listenfd = Tcp_1isten(argv[1], argv[2], NULL);12 else13 err_quit("usage: tcprecv03 [ <host> ] <port#>");14 connfd = Accept(listenfd, NULL, NULL);15 FD_ZERO(&rset);16 FD_ZERO(&xset);17 for (;;) {18 FD_SET(connfd, &rset);19 if (justreadoob == 0)20 FD_SET(connfd, &xset);21 Select(connfd + 1, &rset, NULL, &xset, NULL);22 if (FD_ISSET(connfd, &xset)) {