UNIX: разработка сетевых приложений
0 with bad checksum0 with no checksum832 dropped due to no socket16 broadcast/multicast datagrams dropped due to no socket3941 dropped due to full socket buffers0 not for hashed pcb68419 delivered137685 datagrams outputКлиент отправил 2000 дейтаграмм, но приложение-сервер получило только 30 из них, что означает уровень потерь 98%. Ни сервер, ни клиент не получают сообщения о том, что эти дейтаграммы потеряны. Как мы и говорили, UDP не имеет возможности управления потоком — он ненадежен. Как мы показали, для отправителя UDP не составляет труда переполнить буфер получателя.
Если мы посмотрим на вывод программы
, то увидим, что общее число дейтаграмм, полученных узлом сервера (не приложением-сервером) равно 2000 (73 208 – 71 208). Счетчикnetstat(отброшено из-за переполнения буферов сокета) показывает, сколько дейтаграмм было получено UDP и проигнорировано из-за того, что приемный буфер принимающего сокета был полон [128, с. 775]. Это значение равно 1970 (3941 – 1971), что при добавлении к выводу счетчика дейтаграмм, полученных приложением (30), дает 2000 дейтаграмм, полученных узлом. К сожалению, счетчик дейтаграмм, отброшенных из-за заполненного буфера, в программеdropped due to full socket buffersраспространяется на всю систему. Не существует способа определить, на какие приложения (например, какие порты UDP) это влияет.netstatЧисло дейтаграмм, полученных сервером в этом примере, недетерминировано. Оно зависит от многих факторов, таких как нагрузка сети, загруженность узла клиента и узла сервера.
Если мы запустим тот же клиент и тот же сервер, но на этот раз клиент на медленной системе Sun, а сервер на быстрой системе RS/6000, никакие дейтаграммы не теряются.
aix % <b>udpserv06</b><b>^?</b> <i>после окончания работы клиента вводим наш символ прерывания</i>received 2000 datagramsПриемный буфер сокета UDP
Число дейтаграмм UDP, установленных в очередь UDP, для данного сокета ограничено размером его приемного буфера. Мы можем изменить его с помощью параметра сокета
, как мы показали в разделе 7.5. В FreeBSD по умолчанию размер приемного буфера сокета UDP равен 42 080 байт, что допускает возможность хранения только 30 из наших 1400-байтовых дейтаграмм. Если мы увеличим размер приемного буфера сокета, то можем рассчитывать, что сервер получит дополнительные дейтаграммы. В листинге 8.12 представлена измененная функцияSO_RCVBUFиз листинга 8.10, которая увеличивает размер приемного буфера сокета до 240 Кбайт. Если мы запустим этот сервер в системе Sun, а клиент — в системе RS/6000, то счетчик полученных дейтаграмм будет иметь значение 103. Поскольку это лишь немногим лучше, чем в предыдущем примере с размером буфера, заданным по умолчанию, ясно, что мы пока не получили решения проблемы.dg_echoЛистинг 8.12. Функция dg_echo, увеличивающая размер приемного буфера сокета
//udpcliserv/dgecholоор2.c1 #include "unp.h"2 static void recvfrom_int(int);3 static int count;4 void5 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)6 {7 int n;8 socklen_t len;9 char mesg[MAXLINE];10 Signal(SIGINT, recvfrom_int);11 n = 240 * 1024;12 Setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));13 for (;;) {14 len = clilen;15 Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);16 count++;17 }18 }19 static void20 recvfrom_int(int signo)21 {22 printf("\nreceived %d datagrams\n", count);23 exit(0);24 }ПРИМЕЧАНИЕПочему мы устанавливаем размер буфера приема сокета равным 240×1024 байт в листинге 8.12? Максимальный размер приемного буфера сокета в BSD/OS 2.1 по умолчанию равен 262 144 байта (256×1024), но из-за способа размещения буфера в памяти (описанного в главе 2 [128]) он в действительности ограничен до 246 723 байт. Многие более ранние системы, основанные на 4.3BSD, ограничивали размер буфера приема сокета примерно до 52 000 байт.
8.14. Определение исходящего интерфейса для UDP
С помощью присоединенного сокета UDP можно также задавать исходящий интерфейс, который будет использован для отправки дейтаграмм к определенному получателю. Это объясняется побочным эффектом функции
, примененной к сокету UDP: ядро выбирает локальный IP-адрес (предполагается, что процесс еще не вызвал функциюconnectдля явного его задания). Локальный адрес выбирается в процессе поиска адреса получателя в таблице маршрутизации, причем берется основной IP-адрес интерфейса, с которого, согласно таблице, будут отправляться дейтаграммы.bindВ листинге 8.13 показана простая программа UDP, которая с помощью функции connect соединяется с заданным IP-адресом и затем вызывает функцию
, выводя локальный IP-адрес и порт.getsocknameЛистинг 8.13. Программа UDP, использующая функцию connect для определения исходящего интерфейса
//udpcliserv/udpcli09.c1 #include "unp.h"2 int3 main(int argc, char **argv)4 {5 int sockfd;6 socklen_t len;7 struct sockaddr_in cliaddr, servaddr;8 if (argc != 2)