UNIX: разработка сетевых приложений
8.2. Если протокол использует структуры адреса сокета переменной длины,
может быть слишком длинным. В главе 15 будет показано, что это не вызывает проблем со структурами адреса доменного сокета Unix, но корректным решением будет использовать для функцииclilenфактическую длину, возвращаемую функциейsendto.recvfrom8.4. Запуск программы
с такими параметрами позволяет просмотреть ICMP-сообщения, получаемые узлом, на котором она запущена. Мы используем уменьшенное количество отправляемых пакетов вместо обычного значения 1 пакет в секунду, только чтобы уменьшить объем выводимой на экран информации. Если запустить наш UDP-клиент на узлеping, указав IP-адрес сервера 192.168.42.1, а затем запустить программуsolaris, получим следующий вывод:pingaix % <b>ping -v -I 60 127.0.0.1</b>PING 127.0.0.1: {127.0.0.1}: 56 data bytes64 bytes from 127 0.0.1: icmp_seq=0 ttl=255 time=0 ms36 bytes from 192.168.42.1: Destination Port UnreachableVr HL TOS Len ID Fig Off TTL Pro cks Src Dst Data4 5 00 0022 0007 0 0000 1e 11 c770 192 168 42.2 192.168.42.1UDP: from port 40645. to port 9877 (decimal)ПРИМЕЧАНИЕHe все версии ping выводят сообщения об ICMP-ошибках, даже если задан параметр -v.
8.5. Прослушиваемый сокет может иметь приемный буфер определенного размера, но прослушиваемым TCP-сокетом данные никогда не принимаются. Большинство реализаций не выделяют заранее память под буферы отправки и приема. Размеры буферов сокета, определяемые параметрами
иSO_SNDBUF, являются предельными значениями для соответствующего сокета.SO_RCVBUF8.6. Запустим программу
с параметромsock(использовать UDP) и параметром-u(определяет локальный адрес и порт) на многоинтерфейсном узле-l.freebsdfreebsd % <b>sock -u -l 12.106.32.254.4444 192.168.42.2 8888</b><b>hello</b>Локальный IP-адрес подключен к Интернету (см. рис. 1.7), но чтобы достичь получателя, дейтаграмма должна выйти через другой интерфейс. Наблюдая за сетью с помощью программы
, мы увидим, что IP-адрес отправителя, связанный с клиентом, не является адресом исходящего интерфейса.tcpdump14:28:29.614846 12.106.32.254.444 > 192.168.42.2.8888. udp 614:28:29.615255 192.168.42.2 > 12 106.32.254: icmp: 192.168 42.2udp port 8888 unreachable8.7. Использование функции
на стороне клиента приведет к возникновению задержки между отправками дейтаграмм, что позволит серверу получать большее количество дейтаграмм. Использование функцииprintfна стороне сервера приведет к тому, что сервер будет терять большее количество дейтаграмм.printf8.8. Наибольший размер IPv4-дейтаграммы составляет 65 535 байт и ограничивается 16-разрядным полем полной длины, показанным на рис. А.1. IP-заголовок требует 20 байт, UDP-заголовок — 8 байт, и для пользовательских данных остается не более 65 507 байт. В IPv6 (без поддержки джумбограмм) размер IP-заголовка составляет 40 байт, и под пользовательские данные отводится 65 487 байт.
В листинге Д.3 приведена новая версия
. Если забыть установить размер буфера отправки, Беркли-ядра возвратят из функцииdg_cliошибкуsendto, поскольку размер буфера отправки сокета обычно меньше, чем максимально возможный размер UDP-дейтаграммы (чтобы убедиться в этом, выполните упражнение 7.1).EMSGSIZEЛистинг Д.3. Запись дейтаграммы UDP/IPv4 максимального размера
//udpcliserv/dgclibig.c1 #include "unp.h"2 #undef MAXLINE3 #define MAXLINE 655074 void5 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)6 {7 int size;8 char sendline[MAXLINE], recvline[MAXLINE + 1];9 ssize_t n;10 size = 70000;11 Setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));12 Setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));13 Sendto(sockfd, sendline, MAXLINE, 0, pservaddr, servlen);14 n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);15 printf("received %d bytes\n", n);16 }Но если установить размеры буферов сокета клиента, как показано в листинге Д.3, и запустить программу, сервер ничего не возвратит. С помощью программы
можно убедиться, что клиентская дейтаграмма отправляется серверу, но если в сервер поместить функциюtcpdump, вызов функцииprintfне возвратит дейтаграмму. Проблема заключается в том, что приемный буфер UDP-сокета сервера меньше, чем посланная нами дейтаграмма, поэтому дейтаграмма отбрасывается и не доставляется на сокет. В системах BSD/OS это можно проверить, запустив программуrecvfromи проверив счетчик, указывающий количество дейтаграмм, отброшенных из-за переполнения буферов сокета (netstat -s), до и после получения нашей длинной дейтаграммы. Решением является модификация сервера путем задания размеров буферов приема и отправки сокета.dropped due to full socket buffersВ большинстве сетей дейтаграмма длиной 65 535 байт фрагментируется. Как отмечалось в разделе 2.9, IP-уровнем должен поддерживаться размер буфера для сборки фрагментов, равный всего лишь 576 байт. Поэтому некоторые узлы не получат дейтаграмму максимального размера, посылаемую в данном упражнении. Кроме того, во многих Беркли-реализациях, включая 4.4BSD-Lite2, имеется ошибка, связанная со знаковыми типами данных, которая не позволяет UDP принимать дейтаграммы больше, чем 32 767 байт (см. строка 95, с. 770 [128]).
Глава 9
9.1. В некоторых ситуациях функция
может оказаться очень полезной. Примером приложения, которому может понадобиться эта функция, является традиционный сервер дейтаграмм, обрабатывающий небольшие транзакции, которому периодически приходится устанавливать долговременные соединения. Чаще всего сервер передает одно-два коротких сообщения, но время от времени поступает запрос на проверку базы сервера, и тогда ему приходится передавать большие объемы данных. В такой ситуации имеет смысл отделить ассоциацию, по которой передаются проверочные данные, для обработки ее отдельным процессом или потоком.sctp_peeloff