Книга: 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-20select
, которая ожидает получения либо обычных данных (набор дескрипторов для чтения, 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 }
5justreadoob
, которая указывает, какие данные мы считываем — внеполосные или обычные. Этот флаг определяет, нужно ли вызывать функцию select
для проверки на наличие исключительной ситуации.
26-27
Когда мы устанавливаем флаг justreadoob
, мы также должны выключить бит соответствующего дескриптора в наборе для проверки исключительных ситуаций.
Теперь программа работает так, как мы ожидали.
- Пример 10-25. Создание меню с помощью case
- Пример установочного скрипта
- Пример из практики
- СТРУКТУРА ПРОСТОЙ ПРОГРАММЫ
- ПРИМЕР ПРОСТОЙ ПРОГРАММЫ НА ЯЗЫКЕ СИ
- Аргументы функции в Python
- 3. Функции
- Примеры получения статистики
- Пример применения метода «пять почему»
- Новые функции API для работы с Blob и массивами
- Пример 12-8. Частота встречаемости отдельных слов
- 1.2.5. Пример программы