Чтение онлайн

ЖАНРЫ

UNIX: разработка сетевых приложений
Шрифт:

8.2. Если протокол использует структуры адреса сокета переменной длины,

clilen
может быть слишком длинным. В главе 15 будет показано, что это не вызывает проблем со структурами адреса доменного сокета Unix, но корректным решением будет использовать для функции
sendto
фактическую длину, возвращаемую функцией
recvfrom
.

8.4. Запуск программы

ping
с такими параметрами позволяет просмотреть ICMP-сообщения, получаемые узлом, на котором она запущена. Мы используем уменьшенное количество отправляемых пакетов вместо обычного значения 1 пакет в секунду, только чтобы уменьшить объем выводимой на экран информации.
Если запустить наш UDP-клиент на узле
solaris
, указав IP-адрес сервера 192.168.42.1, а затем запустить программу
ping
, получим следующий вывод:

aix % ping -v -I 60 127.0.0.1

PING 127.0.0.1: {127.0.0.1}: 56 data bytes

64 bytes from 127 0.0.1: icmp_seq=0 ttl=255 time=0 ms

36 bytes from 192.168.42.1: Destination Port Unreachable

Vr HL TOS Len ID Fig Off TTL Pro cks Src Dst Data

4 5 00 0022 0007 0 0000 1e 11 c770 192 168 42.2 192.168.42.1

UDP: from port 40645. to port 9877 (decimal)

ПРИМЕЧАНИЕ

He все версии ping выводят сообщения об ICMP-ошибках, даже если задан параметр -v.

8.5. Прослушиваемый сокет может иметь приемный буфер определенного размера, но прослушиваемым TCP-сокетом данные никогда не принимаются. Большинство реализаций не выделяют заранее память под буферы отправки и приема. Размеры буферов сокета, определяемые параметрами

SO_SNDBUF
и
SO_RCVBUF
, являются предельными значениями для соответствующего сокета.

8.6. Запустим программу

sock
с параметром
– u
(использовать UDP) и параметром
– l
(определяет локальный адрес и порт) на многоинтерфейсном узле
freebsd
.

freebsd % sock -u -l 12.106.32.254.4444 192.168.42.2 8888

hello

Локальный IP-адрес подключен к Интернету (см. рис. 1.7), но чтобы достичь получателя, дейтаграмма должна выйти через другой интерфейс. Наблюдая за сетью с помощью программы

tcpdump
, мы увидим, что IP-адрес отправителя, связанный с клиентом, не является адресом исходящего интерфейса.

14:28:29.614846 12.106.32.254.444 > 192.168.42.2.8888. udp 6

14:28:29.615255 192.168.42.2 > 12 106.32.254: icmp: 192.168 42.2

udp port 8888 unreachable

8.7. Использование функции

printf
на стороне клиента приведет к возникновению задержки между отправками дейтаграмм, что позволит серверу получать большее количество дейтаграмм. Использование функции
printf
на стороне сервера приведет к тому, что сервер будет терять большее количество дейтаграмм.

8.8. Наибольший размер IPv4-дейтаграммы составляет 65 535 байт и ограничивается 16-разрядным полем полной длины, показанным на рис. А.1. IP-заголовок требует 20 байт, UDP-заголовок — 8 байт, и для пользовательских данных остается не более 65 507 байт. В IPv6 (без поддержки джумбограмм) размер IP-заголовка составляет 40 байт, и под пользовательские данные отводится 65 487 байт.

В листинге Д.3 приведена новая версия

dg_cli
. Если забыть установить размер буфера отправки, Беркли-ядра возвратят из функции
sendto
ошибку
EMSGSIZE
, поскольку размер буфера отправки сокета обычно
меньше, чем максимально возможный размер UDP-дейтаграммы (чтобы убедиться в этом, выполните упражнение 7.1).

Листинг Д.3. Запись дейтаграммы UDP/IPv4 максимального размера

//udpcliserv/dgclibig.c

1 #include "unp.h"

2 #undef MAXLINE

3 #define MAXLINE 65507

4 void

5 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
, вызов функции
recvfrom
не возвратит дейтаграмму. Проблема заключается в том, что приемный буфер UDP-сокета сервера меньше, чем посланная нами дейтаграмма, поэтому дейтаграмма отбрасывается и не доставляется на сокет. В системах BSD/OS это можно проверить, запустив программу
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
может оказаться очень полезной. Примером приложения, которому может понадобиться эта функция, является традиционный сервер дейтаграмм, обрабатывающий небольшие транзакции, которому периодически приходится устанавливать долговременные соединения. Чаще всего сервер передает одно-два коротких сообщения, но время от времени поступает запрос на проверку базы сервера, и тогда ему приходится передавать большие объемы данных. В такой ситуации имеет смысл отделить ассоциацию, по которой передаются проверочные данные, для обработки ее отдельным процессом или потоком.

Поделиться с друзьями: