Чтение онлайн

ЖАНРЫ

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

3. Прием входящих соединений: функция

accept
. Если функция
accept
вызывается для блокируемого сокета и новое соединение недоступно, процесс переводится в состояние ожидания.

Если функция

accept
вызывается для неблокируемого сокета и новое соединение недоступно, возвращается ошибка
EWOULDBLOCK
.

4. Инициирование исходящих соединений: функция

connect
для TCP. (Вспомните, что функция connect может использоваться с UDP, но она не вызывает создания «реального» соединения — она лишь заставляет ядро сохранить IP-адрес и номер порта собеседника.) В разделе 2.5 мы показали, что установление соединения TCP включает трехэтапное рукопожатие и что функция connect не возвращает
управление, пока клиент не получит сегмент ACK или SYN. Это значит, что функция TCP
connect
всегда блокирует вызывающий процесс как минимум на время обращения (RTT) к серверу.

Если функция

connect
вызывается для неблокируемого сокета TCP и соединение не может быть установлено немедленно, инициируется установление соединения (например, отправляется первый пакет трехэтапного рукопожатия TCP), но возвращается ошибка
EINPROGRESS
. Обратите внимание, что эта ошибка отличается от ошибки, возвращаемой в первых трех сценариях. Также отметим, что некоторые соединения могут быть установлены немедленно, когда сервер находится на том же узле, что и клиент, поэтому даже в случае неблокируемого вызова функции
connect
мы должны быть готовы к тому, что она успешно выполнится. Пример неблокируемой функции
connect
мы покажем в разделе 16.3.

ПРИМЕЧАНИЕ

Традиционно System V возвращала для неблокируемой операции ввода-вывода, которую невозможно выполнить, ошибку EAGAIN, в то время как Беркли-реализации возвращали ошибку EWOULDBLOCK. Еще больше дело запутывается тем, что согласно POSIX.1 используется EAGAIN, в то время как в POSIX.1g определено, что используется EWOULDBLOCK. К счастью, большинство систем (включая SVR4 и 4.4BSD) определяют один и тот же код для этих двух ошибок (проверьте свой системный заголовочный файл <sys/errno.h>), поэтому не важно, какой из них использовать. В нашем тексте мы используем ошибку EWOULDBLOCK, как определяется в POSIX.

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

connect
.

16.2. Неблокируемые чтение и запись: функция str_cli (продолжение)

Мы снова возвращаемся к нашей функции

str_cli
, которую мы обсуждали в разделах 5.5 и 6.4. Последняя ее версия, задействующая функцию
select
, продолжает использовать блокируемый ввод-вывод. Например, если в стандартном устройстве ввода имеется некоторая строка, мы читаем ее с помощью функции
fgets
и затем отправляем серверу с помощью функции
writen
. Но вызов функции
writen
может вызвать блокирование процесса, если буфер отправки сокета полон. В то время как мы заблокированы в вызове функции
writen
, данные могут быть доступны для чтения из приемного буфера сокета. Аналогично, когда строка ввода доступна из сокета, мы можем заблокироваться в последующем вызове функции
fputs
, если стандартный поток вывода работает медленнее, чем сеть. Наша цель в данном разделе — создать версию этой функции, использующую неблокируемый ввод-вывод. Блокирование будет предотвращено, благодаря чему в это время мы сможем сделать еще что-то полезное.

К сожалению, добавление неблокируемого ввода-вывода значительно усложняет управление буфером функции, поэтому мы будем представлять функцию частями. Мы также заменим вызовы функций из стандартной библиотеки ввода-вывода на обычные

read
и
write
. Это даст возможность отказаться от функций стандартной библиотеки ввода-вывода с неблокируемыми дескрипторами, так как их применение может привести к катастрофическим последствиям.

Мы работаем с двумя буферами: буфер to содержит данные,

направляющиеся из стандартного потока ввода к серверу, а буфер
fr
— данные, приходящие от сервера в стандартный поток вывода. На рис. 16.1 представлена организация буфера
to
и указателей в буфере.

Рис. 16.1. Буфер, содержащий данные из стандартного потока ввода, идущие к сокету

Указатель

toiptr
указывает на следующий байт, в который данные могут быть считаны из стандартного потока ввода. Указатель
tooptr
указывает на следующий байт, который должен быть записан в сокет. Число байтов, которое может быть считано из стандартного потока ввода, равно
&to[MAXLINE]
минус
toiptr
. Как только значение
tooptr
достигает
toiptr
, оба указателя переустанавливаются на начало буфера.

На рис. 16.2 показана соответствующая организация буфера

fr
. В листинге 16.1 [1] представлена первая часть функции.

Рис. 16.2. Буфер, содержащий данные из сокета, идущие к стандартному устройству вывода

Листинг 16.1. Функция str_cli: первая часть, инициализация и вызов функции

//nonblock/strclinonb.c

1 #include "unp.h"

1

Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.

2 void

3 str_cli(FILE *fp, int sockfd)

4 {

5 int maxfdp1, val, stdineof;

6 ssize_t n, nwritten;

7 fd_set rset, wset;

8 char to[MAXLINE], fr[MAXLINE];

9 char *toiptr, *tooptr, *friptr, *froptr;

10 val = Fcntl(sockfd, F_GETFL, 0);

11 Fcntl(sockfd, F_SETFL, val | O_NONBLOCK);

12 val = Fcntl(STDIN_FILENO, F_SETFL, 0);

13 Fcntl(STDIN_FILENO, F_SETFL, val | O_NONBLOCK);

14 val = Fcntl(STDOUT_FILENO, F_SETFL, 0);

15 Fcntl(STDOUT_FILENO, F_SETFL, val | O_NONBLOCK);

16 toiptr = tooptr = to; /* инициализация указателей буфера */

17 friptr = froptr = fr;

18 stdineof = 0;

19 maxfdp1 = max(max(STDIN_FILENO, STDOUT_FILENO), sockfd) + 1;

20 for (;;) {

21 FD_ZERO(&rset);

22 FD_ZERO(&wset);

23 if (stdineof == 0 && toiptr < &to[MAXLINE])

24 FD_SET(STDIN_FILENO, &rset); /* чтение из стандартного потока

ввода */

25 if (friptr < &fr[MAXLINE])

26 FD_SET(sockfd, &rset); /* чтение из сокета */

27 if (tooptr != toiptr)

28 FD_SET(sockfd, &wset); /* данные для записи в сокет */

Поделиться с друзьями: