Литмир - Электронная Библиотека
Содержание  
A
A

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)) {

271
{"b":"225366","o":1}