В листинге 8.8 показан вывод программы
tcpdump
.
Листинг 8.8. Вывод программы tcpdump при запуске функции dg_cli
macosx % <b>tcpdump</b>
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 возвращают.
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)
6 {
7 int i;
8 char sendline[DGLEN];
9 for (i = 0; i < NDG; i++) {
10 Sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);
11 }
12 }
Затем мы изменяем сервер так, чтобы он получал дейтаграммы и считал число полученных дейтаграмм. Сервер больше не отражает дейтаграммы обратно клиенту. В листинге 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)
6 {
7 socklen_t len;
8 char mesg[MAXLINE];
9 Signal (SIGINT, recvfrom_int);
10 for (;;) {
11 len = clilen;
12 Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
13 count++;
14 }
15 }
16 static void
17 recvfrom_int(int signo)
18 {
19 printf("\nreceived %d datagrams\n", count);
20 exit(0);
21 }
Теперь мы запускаем сервер на узле
freebsd
, который представляет собой медленный компьютер SPARCStation. Клиент мы запускаем в значительно более быстрой системе RS/6000 с операционной системой
aix
. Они соединены друг с другом напрямую каналом Ethernet на 100 Мбит/с. Кроме того, мы запускаем программу
netstat -s
на узле сервера и до, и после запуска клиента и сервера, поскольку выводимая статистика покажет, сколько дейтаграмм мы потеряли. В листинге 8.11 показан вывод сервера.
Листинг 8.11. Вывод на узле сервера
freebsd % <b>netstat -s -p udp</b>
udp:
71208 datagrams received
0 with incomplete header
0 with bad data length field
0 with bad checksum
0 with no checksum
832 dropped due to no socket
16 broadcast/multicast datagrams dropped due to no socket
1971 dropped due to full socket buffers
0 not for hashed pcb
68389 delivered
137685 datagrams output
freebsd % <b>udpserv06</b> <i>запускаем наш сервер</i>
<i> клиент посылает дейтаграммы</i>
<b>^C </b><i>для окончания работы клиента вводим наш символ прерывания</i>
freebsd % <b>netstat -s -р udp</b>
udp
73208 datagrams received
0 with incomplete header
0 with bad data length field