Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
16-19
Инициализируются указатели в двух буферах и вычисляется максимальный дескриптор. Это значение, увеличенное на единицу, будет использоваться в качестве первого аргумента функции select
.
20
Как и в случае первой версии этой функции, показанной в листинге 6.2, основной цикл функции содержит вызов функции select
, за которой следуют отдельные проверки различных интересующих нас условий.
21-30
Оба набора дескрипторов обнуляются и затем в каждом наборе включается не более двух битов. Если мы еще не прочитали конец файла из стандартного потока ввода и есть место как минимум для 1 байта данных в буфере to
, то в наборе флагов чтения включается бит, соответствующий стандартному потоку ввода. Если есть место как минимум для 1 байта данных в буфере fr
, то в наборе флагов чтения включается бит, соответствующий сокету. Если есть данные для записи в сокет в буфере to
, то в наборе флагов записи включается бит, соответствующий сокету. Наконец если в буфере fr
есть данные для отправки в стандартный поток вывода, то в наборе флагов записи включается бит, соответствующий этому стандартному потоку.
31
Вызывается функция select
, ожидающая, когда одно из четырех условий станет истинным. Для этой функции мы не задаем тайм-аута.
Следующая часть нашей функции показана в листинге 16.2. Этот код содержит первые две проверки (из четырех возможных), выполняемые после завершения функции select
.
Листинг 16.2. Функция str_cli: вторая часть, чтение из стандартного потока ввода или сокета
//nonblock/strclinonb.c
32 if (FD_ISSET(STDIN_FILENO, &rset)) {
33 if ((n = read(STDIN_FILENO, toiptr, &to[MAXLINE] - toiptr)) < 0) {
34 if (errno != EWOULDBLOCK)
35 err_sys("read error on stdin");
36 } else if (n == 0) {
37 fprintf(stderr, "%s: EOF on stdin\n", gf_time());
38 stdineof = 1; /* с stdin все сделано */
39 if (tooptr == toiptr)
40 Shutdown(sockfd, SHUT_WR); /* отсылаем FIN */
41 } else {
42 fprintf(stderr, "%s: read %d bytes from stdin\n", gf_time(),
43 n);
44 toiptr += n; /* только что полученное из функции read число */
45 FD_SET(sockfd, &wset); /* включаем бит в наборе чтения */
46 }
47 }
48 if (FD_ISSET(sockfd, &rset)) {
49 if ((n = read(sockfd, friptr, &fr[MAXLINE] - friptr)) < 0) {
50 if (errno != EWOULDBLOCK)
51 err_sys("read error on socket");
52 } else if (n == 0) {
53 fprintf(stderr, "%s: EOF on socket\n", gf_time());
54 if (stdineof)
55 return; /* нормальное завершение */
56 else
57 err_quit("str_cli: server terminated prematurely");
58 } else {
59 fprintf(stderr, "%s: read %d bytes from socket\n",
60 gf_time(), n);
61 friptr += n; /* только что полученное из функции read число */
62 FD_SЕТ(STDOUT_FILЕNO, &wset); /* включаем бит в наборе
чтения */
63 }
64 }
32-33
Если стандартный поток ввода готов для чтения, мы вызываем функцию read
. Третий ее аргумент — это количество свободного места в буфере to
.
34-35
Если происходит ошибка EWOULDBLOCK
, мы ничего не предпринимаем. Обычно эта ситуация — когда функция select
сообщает нам о том, что дескриптор готов для чтения, а функция read возвращает ошибку EWOULDBLOCK
— не должна возникать, но тем не менее мы ее обрабатываем.
36-40
Если функция read
возвращает нуль, мы закончили со стандартным потоком ввода. Флаг stdineof
установлен. Если в буфере to больше нет данных для отправки ( tooptr
равно toiptr
), функция shutdown
отправляет серверу сегмент FIN. Если в буфере to
еще есть данные для отправки, сегмент FIN не может быть отправлен до тех пор, пока содержимое буфера не будет записано в сокет.
Мы выводим в стандартный поток сообщений об ошибках строку, отмечающую конец файла, вместе с текущим временем. Мы покажем, как мы используем этот вывод, после описания функции. Аналогичные вызовы функции fprintf выполняются неоднократно в процессе выполнения нашей функции.
41-45
Когда функция read возвращает данные, мы увеличиваем на единицу toiptr
. Мы также включаем бит, соответствующий сокету, в наборе флагов записи, чтобы позже при проверке этого бита в цикле он был включен и тем самым инициировалась бы попытка записи в сокет с помощью функции write
.
Это одно из непростых конструктивных решений, которые приходится принимать при написании кода. У нас есть несколько альтернатив. Вместо установки бита в наборе записи мы можем ничего не делать, и в этом случае функция select будет проверять возможность записи в сокет, когда она будет вызвана в следующий раз. Но это требует дополнительного прохода цикла и вызова функции select, когда мы уже знаем, что у нас есть данные для записи в сокет. Другой вариант — дублировать код, который записывает в сокет, но это кажется расточительным, к тому же это возможный источник ошибки (в случае, если в этой части дублируемого кода есть ошибка и мы обнаруживаем и устраняем ее только в одном месте). Наконец, мы можем создать функцию, записывающую в сокет, и вызывать эту функцию вместо дублирования кода, но эта функция должна использовать три локальные переменные совместно с функцией str_cli, что может привести к необходимости сделать эти переменные глобальными. Выбор, сделанный в нашем случае, — это результат субъективного мнения автора относительно того, какой из описанных трех вариантов предпочтительнее.
48-64
Эти строки кода аналогичны выражению if
, только что описанному для случая, когда стандартный поток ввода готов для чтения. Если функция read
возвращает ошибку EWOULDBLOCK
, ничего не происходит. Если мы встречаем признак конца файла, присланный сервером, это нормально, когда мы уже получили признак конца файла в стандартном потоке ввода. Но иначе это будет ошибкой, означающей преждевременное завершение работы сервера ( Server terminated prematurely
). Если функция read
возвращает некоторые данные, friptr
увеличивается на единицу и в наборе флагов записи включается бит для стандартного потока вывода, с тем чтобы попытаться записать туда данные в следующей части функции.
В листинге 16.3 показана последняя часть нашей функции.
Листинг 16.3. Функция str_cli: третья часть, запись в стандартный поток вывода или сокет
//nonblock/strclinonb.c
65 if (FD_ISSET(STDOUT_FILENO, &wset) && ((n = friptr - froptr) > 0)) {
Интервал:
Закладка: