Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Еще один пример долгосрочного взаимодействия — это DNS (рис. 8.8).

Рис. 8.8. Пример клиентов и серверов DNS и функции connect
Клиент DNS может быть сконфигурирован для использования одного или более серверов, обычно с помощью перечисления IP-адресов серверов в файле /etc/resolv.conf
. Если в этом файле указан только один сервер (на рисунке этот клиент изображен в крайнем слева прямоугольнике), клиент может вызвать функцию connect, но если перечислено множество серверов (второй справа прямоугольник на рисунке), клиент не может вызвать функцию connect
. Обычно сервер DNS обрабатывает также любые клиентские запросы, следовательно, серверы не могут вызывать функцию connect
.
Многократный вызов функции connect для сокета UDP
Процесс с присоединенным сокетом UDP может снова вызвать функцию connect
Для этого сокета, чтобы:
■ задать новый IP-адрес и порт;
■ отсоединить сокет.
Первый случай, задание нового собеседника для присоединенного сокета UDP, отличается от использования функции connect
с сокетом TCP: для сокета TCP функция connect
может быть вызвана только один раз.
Чтобы отсоединить сокет UDP, мы вызываем функцию connect
, но присваиваем элементу семейства структуры адреса сокета ( sin_family
для IPv4 или sin6_family
для IPv6) значение AF_UNSPEC
. Это может привести к ошибке EAFNOSUPPORT
[128, с. 736], но это нормально. Именно процесс вызова функции connect
на уже присоединенном сокете UDP позволяет отсоединить сокет [128, с. 787–788].
В руководстве BSD по поводу функции connect традиционно говорилось: «Сокеты дейтаграмм могут разрывать связь, соединяясь с недействительными адресами, такими как пустые адреса». К сожалению, ни в одном руководстве не сказано, что представляет собой «пустой адрес», и не упоминается, что в результате возвращается ошибка (что нормально). Стандарт POSIX явно указывает, что семейство адресов должно быть установлено в AF_UNSPEC, но затем сообщает, что этот вызов функции connect может возвратить, а может и не возвратить ошибку EAFNOSUPPORT.
Производительность
Когда приложение вызывает функцию sendto
на неприсоединенном сокете UDP, ядра реализаций, происходящих от Беркли, временно соединяются с сокетом, отправляют дейтаграмму и затем отсоединяются от сокета [128, с. 762–763]. Таким образом, вызов функции sendto
для последовательной отправки двух дейтаграмм на неприсоединенном сокете включает следующие шесть шагов, выполняемых ядром:
■ присоединение сокета;
■ вывод первой дейтаграммы;
■ отсоединение сокета;
■ присоединение сокета;
■ вывод второй дейтаграммы;
■ отсоединение сокета.
Другой момент, который нужно учитывать, — количество поисков в таблице маршрутизации. Первое временное соединение производит поиск в таблице маршрутизации IP-адреса получателя и сохраняет (кэширует) эту информацию. Второе временное соединение отмечает, что адрес получателя совпадает с кэшированным адресом из таблицы маршрутизации (мы считаем, что обеим функциям sendto задан один и тот же получатель), и ему не нужно снова проводить поиск в таблице маршрутизации [128, с. 737–738].
Когда приложение знает, что оно будет отправлять множество дейтаграмм одному и тому же собеседнику, эффективнее будет присоединить сокет явно. Вызов функции connect
, за которым следуют два вызова функции write
, теперь будет включать следующие шаги, выполняемые ядром:
■ присоединение сокета;
■ вывод первой дейтаграммы;
■ вывод второй дейтаграммы.
В этом случае ядро копирует структуру адреса сокета, содержащую IP-адрес получателя и порт, только один раз, а при двойном вызове функции sendto
копирование выполняется дважды. В [89] отмечается, что на временное присоединение отсоединенного сокета UDP приходится примерно треть стоимости каждой передачи UDP.
8.12. Функция dg_cli (продолжение)
Вернемся к функции dg_cli
, показанной в листинге 8.4, и перепишем ее, с тем чтобы она вызывала функцию connect
. В листинге 8.7 показана новая функция.
Листинг 8.7. Функция dg_cli, вызывающая функцию connect
//udpcliserv/dgcliconnect.c
1 #include "unp.h"
2 void
3 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
4 {
5 int n;
6 char sendline[MAXLINE], recvline[MAXLINE + 1];
7 Connect(sockfd, (SA*)pservaddr, servlen);
8 while (Fgets(sendline, MAXLINE, fp) != NULL) {
9 Write(sockfd, sendline, strlen(sendline));
10 n = Read(sockfd, recvline, MAXLINE);
11 recvline[n] = 0; /* завершающий нуль */
12 Fputs(recvline, stdout);
13 }
14 }
Изменения по сравнению с предыдущей версией — это добавление вызова функции connect
и замена вызовов функций sendto
и recvfrom вызовами функций write
и read
. Функция dg_cli
остается не зависящей от протокола, поскольку она не вникает в структуру адреса сокета, передаваемую функции connect
. Наша функция main
клиента, показанная в листинге 8.3, остается той же.
Если мы запустим программу на узле macosx
, задав IP-адрес узла freebsd4
(который не запускает наш сервер на порте 9877), мы получим следующий вывод:
macosx % udpcli04 172.24.37.94
hello, world
read error: Connection refused
Первое, что мы замечаем, — мы не получаем ошибку, когда запускаем процесс клиента. Ошибка происходит только после того, как мы отправляем серверу первую дейтаграмму. Именно отправка этой дейтаграммы вызывает ошибку ICMP от узла сервера. Но когда клиент TCP вызывает функцию connect
, задавая узел сервера, на котором не запущен процесс сервера, функция connect
возвращает ошибку, поскольку вызов функции connect
вызывает отправку первого пакета трехэтапного рукопожатия TCP, и именно этот пакет вызывает получение сегмента RST от собеседника (см. раздел 4.3).
В листинге 8.8 показан вывод программы tcpdump
.
Листинг 8.8. Вывод программы tcpdump при запуске функции dg_cli
macosx % tcpdump
01 0.0 macosx.51139 > freebsd4 9877:udp 13
02 0.006180 ( 0.0062) freebsd4 > macosx: icmp: freebsd4 udp port 9877 unreachable
В табл. A.5 мы также видим, что возникшую ошибку ICMP ядро сопоставляет ошибке ECONNREFUSED
, которая соответствует выводу строки сообщения Connection refused
(В соединении отказано) функцией err_sys
.
К сожалению, не все ядра возвращают сообщения ICMP присоединенному сокету UDP, как мы показали в этом разделе. Обычно ядра реализаций, происходящих от Беркли, возвращают эту ошибку, а ядра System V — не возвращают. Например, если мы запустим тот же клиент на узле Solaris 2.4 и с помощью функции connect соединимся с узлом, на котором не запущен наш сервер, то с помощью программы tcpdump мы сможем убедиться, что ошибка ICMP о недоступности порта возвращается узлом сервера, но вызванная клиентом функция read никогда не завершается. Эта ситуация была исправлена в Solaris 2.5. UnixWare не возвращает ошибку, в то время как AIX, Digital Unix, HP-UX и Linux возвращают.
Читать дальшеИнтервал:
Закладка: