Изменения по сравнению с предыдущей версией — это добавление вызова функции
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
В табл. 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 возвращают.
8.13. Отсутствие управления потоком в UDP
Теперь мы проверим, как влияет на работу приложения отсутствие какого-либо управления потоком в UDP. Сначала мы изменим нашу функцию
dg_cli
так, чтобы она отправляла фиксированное число дейтаграмм. Она больше не будет читать из стандартного потока ввода. В листинге 8.9 показана новая версия функции. Эта функция отправляет серверу 2000 дейтаграмм UDP по 1400 байт каждая.
Листинг 8.9. Функция dg_cli, отсылающая
фиксированное число дейтаграмм серверу
//udpcliserv/dgcliloop1.c
1 #include "unp.h"
2 #define NDG 2000 /* количество дейтаграмм для отправки */
3 #define DGLEN 1400 /* длина каждой дейтаграммы */
4 void
5 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
Затем мы изменяем сервер так, чтобы он получал дейтаграммы и считал число полученных дейтаграмм. Сервер больше не отражает дейтаграммы обратно клиенту. В листинге 8.10 показана новая функция
dg_echo
. Когда мы завершаем процесс сервера нажатием клавиши прерывания на терминале (что приводит к отправке сигнала
SIGINT
процессу), сервер выводит число полученных дейтаграмм и завершается.
Листинг 8.10. Функция dg_echo, считающая полученные дейтаграммы
//udpcliserv/dgecholoop1.c
1 #include "unp.h"
2 static void recvfrom_int(int);
3 static int count;
4 void
5 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
, который представляет собой медленный компьютер SPARCStation. Клиент мы запускаем в значительно более быстрой системе RS/6000 с операционной системой
aix
. Они соединены друг с другом напрямую каналом Ethernet на 100 Мбит/с. Кроме того, мы запускаем программу
netstat -s
на узле сервера и до, и после запуска клиента и сервера, поскольку выводимая статистика покажет, сколько дейтаграмм мы потеряли. В листинге 8.11 показан вывод сервера.