read 2 bytes: 89

received EOF

Результаты оказались такими, как мы и ожидали. Каждый раз, когда отправитель посылает внеполосные данные, для получателя генерируется сигнал SIGURG, после чего получатель считывает один байт, содержащий внеполосные данные.

Простой пример использования функции 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: %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: 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 &quot;unp.h&quot;

 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(&quot;usage: tcprecv03 [ &lt;host&gt; ] &lt;port#&gt;&quot;);

14  connfd = Accept(listenfd, NULL, NULL);

15  FD_ZERO(&amp;rset);

16  FD_ZERO(&amp;xset);

17  for (;;) {

18   FD_SET(connfd, &amp;rset);

19   if (justreadoob == 0)

20    FD_SET(connfd, &amp;xset);

21   Select(connfd + 1, &amp;rset, NULL, &amp;xset, NULL);

22   if (FD_ISSET(connfd, &amp;xset)) {


Перейти на страницу:
Изменить размер шрифта: