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

Пример: дополнительные свойства внеполосных данных

Пример: дополнительные свойства внеполосных данных

Теперь мы покажем другой столь же простой пример, иллюстрирующий две дополнительные особенности внеполосных данных, о которых мы уже упоминали ранее.

1. TCP посылает уведомление об отправке внеполосных данных (их срочный указатель), даже если поток данных остановлен функциями управления потоком.

2. Принимающий процесс может получить уведомление о том, что отправитель отослал внеполосные данные (с помощью сигнала SIGURG или функции select) до того, как эти данные фактически прибудут. Если после получения этого уведомления процесс вызывает функцию recv, задавая флаг MSG_OOB, а внеполосные данные еще не прибыли, то будет возвращена ошибка EWOULDBLOCK.

В листинге 24.8 приведена программа отправки.

Листинг 24.8. Программа отправки

//oob/tcpsend05.c
 1 #include "unp.h"
 2 int
 3 main(int argc, char **argv)
 4 {
 5  int sockfd, size;
 6  char buff[16384];
 7  if (argc != 3)
 8   err_quit("usage: tcpsend04 <host> <port#>");
 9  sockfd = Tcp_connect(argv[1], argv[2]);
10  size = 32768;
11  Setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
12  Write(sockfd, buff, 16384);
13  printf("wrote 16384 bytes of normal datan");
14  sleep(5);
15  Send(sockfd, "a", 1, MSG_OOB);
16  printf("wrote 1 byte of OOB datan");
17  Write(sockfd, buff, 1024);
18  printf("wrote 1024 bytes of normal datan");
19  exit(0);
20 }
9-19
 Этот процесс устанавливает размер буфера отправки сокета равным 32 768 байт, записывает 16 384 байт обычных данных, а затем на 5 с переходит в спящее состояние. Чуть ниже мы увидим, что приемник устанавливает размер приемного буфера сокета равным 4096 байт, поэтому данные, отправленные отсылающим TCP, с гарантией заполнят приемный буфер сокета получателя. Затем отправитель посылает один байт внеполосных данных, за которым следуют 1024 байт обычных данных, и, наконец, закрывает соединение.

В листинге 24.9 представлена принимающая программа.

Листинг 24.9. Принимающая программа

//oob/tcprecv05.c
 1 #include "unp.h"
 2 int listenfd, connfd;
 3 void sig_urg(int);
 4 int
 5 main(int argc, char **argv)
 6 {
 7  int size;
 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: tcprecv05 [ <host> ] <port#>");
14  size = 4096;
15  Setsockopt(listenfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
16  connfd = Accept(listenfd, NULL, NULL);
17  Signal(SIGURG, sig_urg);
18  Fcntl(connfd, F_SETOWN, getpid());
19  for (;;)
20   pause();
21 }
22 void
23 sig_urg(int signo)
24 {
25  int n;
26  char buff[2048];
27  printf("SIGURG receivedn");
28  n = Recv(connfd, buff, sizeof(buff) - 1, MSG_OOB);
29  buff[n] = 0; /* завершающий пустой байт */
30  printf("read %d OOB byten", n);
31 }
14-20
 Принимающий процесс устанавливает размер приемного буфера сокета приемника равным 4096 байт. Этот размер наследуется присоединенным сокетом после установления соединения. Затем процесс вызывает функцию accept, задает обработчик для сигнала SIGURG и задает владельца сокета. В главном цикле (бесконечном) вызывается функция pause.

22-31 Обработчик сигнала вызывает функцию recv для считывания внеполосных данных.

Если мы запускаем сначала принимающую программу, а затем программу отправки, то получаем следующий результат выполнения программы отправки:

macosx % tcpsend05 freebsd 5555
wrote 16384 bytes of normal data
wrote 1 byte of OOB data
wrote 1024 bytes of normal data

Как и ожидалось, все данные помещаются в буфер отправки сокета отправителя, и программа завершается. Ниже приведен результат работы принимающей программы:

freebsd4 % tcprecv05 5555
SIGURG received
recv error: Resource temporarily unavailable

Сообщение об ошибке, которое выдает наша функция err_sys, соответствует ошибке EAGAIN, которая в FreeBSD аналогична ошибке EWOULDBLOCK. TCP посылает уведомление об отправке внеполосных данных принимающему TCP, который в результате генерирует сигнал SIGURG для принимающего процесса. Но когда вызывается функция recv и задается флаг MSG_OOB, байт с внеполосными данными не может быть прочитан.

Для решения этой проблемы необходимо, чтобы получатель освобождал место в своем приемном буфере, считывая поступившие обычные данные. В результате TCP объявит для отправителя окно ненулевого размера, что в конечном счете позволит отправителю передать байт, содержащий внеполосные данные.

ПРИМЕЧАНИЕ

В реализациях, происходящих от Беркли [128, с. 1016-1017], можно отметить две близких проблемы. Во-первых, даже если приемный буфер сокета заполнен, ядро всегда принимает от процесса внеполосные данные для отправки собеседнику. Во-вторых, когда отправитель посылает байт с внеполосными данными, немедленно посылается сегмент TCP, содержащий срочное уведомление. Все обычные проверки вывода TCP (алгоритм Нагла, предотвращение синдрома «глупого окна») при этом блокируются.

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


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