13 Write(sockfd, "5", 1);
14 printf("wrote 1 byte of normal data\n");
15 exit(0);
16 }
Листинг 24.7. Принимающая программа, в которой вызывается функция sokatmark
//oob/tcprecv04.c
1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4 {
5 int listenfd, connfd, n, on = 1;
6 char buff[100];
7 if (argc == 2)
8 listenfd = Tcp_listen(NULL, argv[1], NULL);
9 else if (argc == 3)
10 listenfd = Tcp_listen(argv[1], argv[2], NULL);
11 else
12 err_quit("usage- tcprecv04 [ <host> ] <port#>");
13 Setsockopt(listenfd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
14 connfd = Accept(listenfd, NULL, NULL);
15 sleep(5);
16 for (;;) {
17 if (Sockatmark(connfd))
18 printf("at OOB mark\n");
19 if ((n = Read(connfd, buff, sizeof(buff) - 1)) == 0) {
20 printf("received EOF\n");
21 exit(0);
22 }
23 buff[n] = 0; /* завершающий нуль */
24 printf("read %d bytes: %s\n", n; buff);
25 }
26 }
Включение параметра сокета SO_OOBINLINE
13
Мы хотим принимать внеполосные данные вместе с обычными данными, поэтому нам нужно включить параметр
SO_OOBINLINE
. Но если мы будем ждать, когда выполнится функция accept и установит этот параметр для присоединенного сокета, трехэтапное рукопожатие завершится и внеполосные данные могут уже прибыть. Поэтому нам нужно установить этот параметр еще для прослушиваемого сокета, помня о том, что все параметры прослушиваемого сокета наследуются присоединенным сокетом (см. раздел 7.4).
Вызов функции sleep после вызова функции accept
14-15
После того как выполнена функция
accept
, получатель переходит в спящее состояние, что позволяет получить все данные, посланные отправителем. Это позволяет нам продемонстрировать, что функция read останавливается на отметке внеполосных данных, даже если в приемном буфере сокета имеются дополнительные данные.
Считывание всех отправленных данных
16-25
В программе имеется цикл, в котором вызывается функция
read
и выводятся полученные данные. Но перед вызовом функции
read
функция
sockatmark
проверяет, находится ли указатель буфера на отметке внеполосных данных.
После выполнения этой программы мы получаем следующий результат:
freebsd4 % <b>tcprecv04 6666</b>
read 3 bytes: 123
at OOB mark
read 2 bytes: 45
received EOF
Хотя принимающий TCP получил все посланные данные, первый вызов функции
read
возвращает только три байта, так как была обнаружена отметка внеполосных данных. Следующий считанный байт — это байт, содержащий внеполосные данные (его значение равно 4), так как мы дали ядру указание поместить внеполосные данные вместе с обычными.
Пример: дополнительные свойства внеполосных данных
Теперь мы покажем другой столь же простой пример, иллюстрирующий две дополнительные особенности внеполосных данных, о которых мы уже упоминали ранее.
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 data\n");
14 sleep(5);
15 Send(sockfd, "a", 1, MSG_OOB);
16 printf("wrote 1 byte of OOB data\n");
17 Write(sockfd, buff, 1024);
18 printf("wrote 1024 bytes of normal data\n");