Книга: UNIX: разработка сетевых приложений

Простой пример использования функции select

Простой пример использования функции select

Теперь мы переделаем код нашего получателя внеполосных данных и вместо сигнала SIGURG будем использовать функцию select. В листинге 24.3 показана принимающая программа.

Листинг 24.3. Принимающая программа, в которой (ошибочно) используется функция select для уведомления о получении внеполосных данных

//oob/tcprecv02.c
 1 #include "unp.h"
 2 int
 3 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  else
13   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: %sn", n, buff);
25   }
26   if (FD_ISSET(connfd, &rset)) {
27    if ((n = Read(connfd, buff, sizeof(buff) - 1)) == 0) {
28     printf("received EOFn");
29     exit(0);
30    }
31    buff[n] = 0; /* завершающий нуль */
32    printf("read bytes: %sn", n, buff);
33   }
34  }
35 }
15-20
 Процесс вызывает функцию select, которая ожидает получения либо обычных данных (набор дескрипторов для чтения, rset), либо внеполосных (набор дескрипторов для обработки исключений, xset). В обоих случаях полученные данные выводятся.

Если мы запустим эту программу, а затем — программу для отправки, которая приведена в листинге 24.1, то столкнемся со следующей ошибкой:

freebsd4 % tcprecv02 9999
read 3 bytes: 123
read 1 OOB byte: 4
recv error: Invalid argument

Проблема заключается в том, что функция select будет сообщать об исключительной ситуации, пока процесс не считает данные, находящиеся за отметкой внеполосных данных (то есть после них [128, с. 530-531]). Мы не можем считывать внеполосные данные больше одного раза, так как после первого же их считывания ядро очищает буфер, содержащий один байт внеполосных данных. Когда мы вызываем функцию recv, устанавливая флаг MSG_OOB во второй раз, она возвращает ошибку EINVAL.

Чтобы решить эту проблему, нужно вызывать функцию select для проверки на наличие исключительной ситуации только после того, как будут приняты все обычные данные. В листинге 24.4 показана модифицированная версия принимающей программы из листинга 24.3. В этой версии описанный сценарий обрабатывается корректно.

Листинг 24.4. Модификация программы, приведенной в листинге 24.3. Функция select применяется для проверки исключительной ситуации корректным образом

//oob/tcprecv03.c
 1 #include "unp.h"
 2 int
 3 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  else
13   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)) {
23    n = Recv(connfd, buff, sizeof(buff) - 1, MSG_OOB);
24    buff[n] = 0; /* завершающий нуль */
25    printf("read %d OOB byte: %sn", 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 EOFn");
32     exit(0);
33    }
34    buff[n] = 0; /* завершающий нуль */
35    printf("read %d bytes: %sn", n, buff);
36    justreadoob = 0;
37   }
38  }
39 }
5
 Мы объявляем новую переменную с именем justreadoob, которая указывает, какие данные мы считываем — внеполосные или обычные. Этот флаг определяет, нужно ли вызывать функцию select для проверки на наличие исключительной ситуации.

26-27 Когда мы устанавливаем флаг justreadoob, мы также должны выключить бит соответствующего дескриптора в наборе для проверки исключительных ситуаций.

Теперь программа работает так, как мы ожидали.

Оглавление книги


Генерация: 1.186. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз