Другой момент, который нужно учитывать, — количество поисков в таблице маршрутизации. Первое временное соединение производит поиск в таблице маршрутизации IP-адреса получателя и сохраняет (кэширует) эту информацию. Второе временное соединение отмечает, что адрес получателя совпадает с кэшированным адресом из таблицы маршрутизации (мы считаем, что обеим функциям sendto задан один и тот же получатель), и ему не нужно снова проводить поиск в таблице маршрутизации [128, с. 737–738].
Когда приложение знает, что оно будет отправлять множество дейтаграмм одному и тому же собеседнику, эффективнее будет присоединить сокет явно. Вызов функции connect
write
, теперь будет включать следующие шаги, выполняемые ядром:■ присоединение сокета;
■ вывод первой дейтаграммы;
■ вывод второй дейтаграммы.
В этом случае ядро копирует структуру адреса сокета, содержащую IP-адрес получателя и порт, только один раз, а при двойном вызове функции sendto
8.12. Функция dg_cli (продолжение)
Вернемся к функции dg_cli
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
freebsd4
(который не запускает наш сервер на порте 9877), мы получим следующий вывод:macosx % udpcli04 172.24.37.94
hello, world
read error: Connection refused
Первое, что мы замечаем, — мы
connect
, задавая узел сервера, на котором не запущен процесс сервера, функция connect
возвращает ошибку, поскольку вызов функции connect
вызывает отправку первого пакета трехэтапного рукопожатия TCP, и именно этот пакет вызывает получение сегмента RST от собеседника (см. раздел 4.3).В листинге 8.8 показан вывод программы tcpdump
Листинг 8.8
. Вывод программы tcpdump при запуске функции dg_climacosx % 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
.