KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программное обеспечение » Уильям Стивенс - UNIX: разработка сетевых приложений

Уильям Стивенс - UNIX: разработка сетевых приложений

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Уильям Стивенс, "UNIX: разработка сетевых приложений" бесплатно, без регистрации.
Перейти на страницу:

1. Мы больше не можем задавать IP-адрес получателя и порт для операции вывода. То есть мы используем вместо функции sendto функцию write или send. Все, что записывается в присоединенный сокет UDP, автоматически отправляется на адрес (например, IP-адрес и порт), заданный функцией connect.

ПРИМЕЧАНИЕ

Аналогично TCP, мы можем вызвать функцию sendto для присоединенного сокета UDP, но не можем задать адрес получателя. Пятый аргумент функции sendto (указатель на структуру адреса сокета) должен быть пустым указателем, а шестой аргумент (размер структуры адреса сокета) должен быть нулевым. В стандарте POSIX определено, что когда пятый аргумент является пустым указателем, шестой аргумент игнорируется.

2. Вместо функции recvfrom мы используем функцию read или recv. Единственные дейтаграммы, возвращаемые ядром для операции ввода через присоединенный сокет UDP, — это дейтаграммы, приходящие с адреса, заданного в функции connect. Дейтаграммы, предназначенные для адреса локального протокола присоединенного сокета UDP (например, IP-адрес и порт), но приходящие с адреса протокола, отличного от того, к которому сокет был присоединен с помощью функции connect, не передаются присоединенному сокету. Это ограничивает присоединенный сокет UDP, позволяя ему обмениваться дейтаграммами с одним и только одним собеседником.

ПРИМЕЧАНИЕ

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

3. Асинхронные ошибки возвращаются процессу только при операциях с присоединенным сокетом UDP. В результате, как мы уже говорили, неприсоединенный сокет UDP не получает никаких асинхронных ошибок.

В табл. 8.2 сводятся воедино свойства, перечисленные в первом пункте, применительно к 4.4BSD.


Таблица 8.2. Сокеты TCP и UDP: может ли быть задан адрес протокола получателя

Тип сокета write или send sendto, без указания получателя sendto, с указанием получателя Сокет TCP Да Да EISCONN Сокет UDP, присоединенный Да Да EISCONN Сокет UDP, неприсоединенный EDESTADDRREQ EDESTADDRREQ Да ПРИМЕЧАНИЕ

POSIX определяет, что операция вывода, не задающая адрес получателя на неприсоединенном сокете UDP, должна возвращать ошибку ENOTCONN, а не EDESTADDRREQ.

Solaris 2.5 допускает функцию sendto, которая задает адрес получателя для присоединенного сокета UDP. POSIX определяет, что в такой ситуации должна возвращаться ошибка EISCONN.

На рис. 8.7 обобщается информация о присоединенном сокете UDP.

Рис. 8.7. Присоединенный сокет UDP

Приложение вызывает функцию connect, задавая IP-адрес и номер порта собеседника. Затем оно использует функции read и write для обмена данными с собеседником.

Дейтаграммы, приходящие с любого другого IP-адреса или порта (который мы обозначаем как «???» на рис. 8.7), не передаются на присоединенный сокет, поскольку либо IP-адрес, либо UDP-порт отправителя не совпадают с адресом протокола, с которым сокет соединяется с помощью функции connect. Эти дейтаграммы могут быть доставлены на какой-то другой сокет UDP на узле. Если нет другого совпадающего сокета для приходящей дейтаграммы, UDP проигнорирует ее и сгенерирует ICMP-сообщение о недоступности порта.

Обобщая вышесказанное, мы можем утверждать, что клиент или сервер UDP может вызвать функцию connect, только если этот процесс использует сокет UDP для связи лишь с одним собеседником. Обычно именно клиент UDP вызывает функцию connect, но существуют приложения, в которых сервер UDP связывается с одним клиентом на длительное время (например, TFTP), и в этом случае и клиент, и сервер вызывают функцию connect.

Еще один пример долгосрочного взаимодействия — это 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)

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*