Страница 102 из 399
Рис. 8.8. Пример клиентов и серверов DNS и функции co
Клиент DNS может быть сконфигурирован для использования одного или более серверов, обычно с помощью перечисления IP-адресов серверов в файле /etc/resolv.conf. Если в этом файле указан только один сервер (на рисунке этот клиент изображен в крайнем слева прямоугольнике), клиент может вызвать функцию co
Многократный вызов функции co
Процесс с присоединенным сокетом UDP может снова вызвать функцию co
■ задать новый IP-адрес и порт;
■ отсоединить сокет.
Первый случай, задание нового собеседника для присоединенного сокета UDP, отличается от использования функции co
Чтобы отсоединить сокет UDP, мы вызываем функцию co
В руководстве BSD по поводу функции co
Производительность
Когда приложение вызывает функцию sendto на неприсоединенном сокете UDP, ядра реализаций, происходящих от Беркли, временно соединяются с сокетом, отправляют дейтаграмму и затем отсоединяются от сокета [128, с. 762–763]. Таким образом, вызов функции sendto для последовательной отправки двух дейтаграмм на неприсоединенном сокете включает следующие шесть шагов, выполняемых ядром:
■ присоединение сокета;
■ вывод первой дейтаграммы;
■ отсоединение сокета;
■ присоединение сокета;
■ вывод второй дейтаграммы;
■ отсоединение сокета.
Другой момент, который нужно учитывать, — количество поисков в таблице маршрутизации. Первое временное соединение производит поиск в таблице маршрутизации IP-адреса получателя и сохраняет (кэширует) эту информацию. Второе временное соединение отмечает, что адрес получателя совпадает с кэшированным адресом из таблицы маршрутизации (мы считаем, что обеим функциям sendto задан один и тот же получатель), и ему не нужно снова проводить поиск в таблице маршрутизации [128, с. 737–738].
Когда приложение знает, что оно будет отправлять множество дейтаграмм одному и тому же собеседнику, эффективнее будет присоединить сокет явно. Вызов функции co
■ присоединение сокета;
■ вывод первой дейтаграммы;
■ вывод второй дейтаграммы.
В этом случае ядро копирует структуру адреса сокета, содержащую IP-адрес получателя и порт, только один раз, а при двойном вызове функции sendto копирование выполняется дважды. В [89] отмечается, что на временное присоединение отсоединенного сокета UDP приходится примерно треть стоимости каждой передачи UDP.
8.12. Функция dg_cli (продолжение)
Вернемся к функции dg_cli, показанной в листинге 8.4, и перепишем ее, с тем чтобы она вызывала функцию co
Листинг 8.7. Функция dg_cli, вызывающая функцию co
//udpcliserv/dgclico
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 Co
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 }
Изменения по сравнению с предыдущей версией — это добавление вызова функции co
Если мы запустим программу на узле macosx, задав IP-адрес узла freebsd4 (который не запускает наш сервер на порте 9877), мы получим следующий вывод:
macosx % <b>udpcli04 172.24.37.94</b>
<b>hello, world</b>
read error: Co
Первое, что мы замечаем, — мы не получаем ошибку, когда запускаем процесс клиента. Ошибка происходит только после того, как мы отправляем серверу первую дейтаграмму. Именно отправка этой дейтаграммы вызывает ошибку ICMP от узла сервера. Но когда клиент TCP вызывает функцию co