355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Уильям Ричард Стивенс » UNIX: разработка сетевых приложений » Текст книги (страница 24)
UNIX: разработка сетевых приложений
  • Текст добавлен: 17 сентября 2016, 20:42

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


Автор книги: Уильям Ричард Стивенс


Соавторы: Эндрю М. Рудофф,Билл Феннер

Жанр:

   

ОС и Сети


сообщить о нарушении

Текущая страница: 24 (всего у книги 88 страниц) [доступный отрывок для чтения: 32 страниц]

Параметр сокета SCTP_RTOINFO

Параметр используется для считывания и установки различных тайм-аутов для конкретной ассоциации или используемых по умолчанию для конечной точки. Для считывания параметров по соображениям переносимости следует использовать функцию sctp_opt_info, а не getsockopt. Перед вызовом необходимо заполнить структуру sctp_rtoinfo, которая определяется следующим образом:

struct sctp_rtoinfo {

 sctp_assoc_t srto_assoc_id;

 uint32_t srto_initial;

 uint32_t srto_max;

 uint32_t srto_min;

};

Поля структуры имеют следующий смысл:

■  srto_assoc_idсодержит либо идентификатор конкретной ассоциации, либо 0. В последнем случае работа осуществляется со значениями по умолчанию;

■  srto_initialхранит начальное значение RTO для конкретного адреса собеседника. Это значение используется при отправке порции INIT. Измеряется поле в миллисекундах и по умолчанию равно 3000;

■  srto_maxсодержит максимальное значение RTO, используемое при изменении таймера повторной передачи. Если рассчитанное значение оказывается больше максимального RTO, в качестве нового тайм-аута используется именно максимальное значение. По умолчанию это поле имеет значение 60 000 мс;

■  srto_minсодержит минимальное значение RTO, используемое при первом запуске таймера повторной передачи. Когда таймер RTO изменяется, новое значение обязательно сравнивается с минимальным. По умолчанию это поле имеет значение 1000 мс.

Запись 0 в поля srto_initial, srto_maxи srto_minозначает, что менять текущие параметры по умолчанию не требуется. Все значения измеряются в миллисекундах. Руководство по установке таймеров для достижения максимальной производительности приводится в разделе 23.11.

Параметр сокета SCTP_SET_PEER_PRIMARY_ADDR

Установка этого параметра приводит к отправке собеседнику сообщения, запрашивающего установку конкретного локального адреса в качестве основного. Процесс должен заполнить структуру sctp_setpeerprimи указать в ней идентификатор ассоциации и локальный адрес, который должен быть сделан основным. Этот адрес должен быть привязан к данной конечной точке. Структура sctp_setpeerprimопределяется следующим образом:

struct sctp_setpeerprim {

 sctp_assoc_t sspp_assoc_id;

 struct sockaddr_storage sspp_addr;

};

Ниже приводится описание полей структуры.

■  sspp_assoc_idуказывает идентификатор ассоциации, для которой требуется установить новый основной адрес. При работе с сокетом типа «один-к-одному» это поле игнорируется;

■  sspp_addrсодержит локальный адрес, который должен использоваться собеседником в качестве основного.

Поддержка этой функции SCTP не является обязательной. Если локальная конечная точка не поддерживает параметр, процессу будет возвращена ошибка EOPNOTSUPP. Если же параметр не поддерживается удаленной конечной точкой, ошибка будет другой: EINVAL. Обратите внимание, что данный параметр не может использоваться для считывания основного адреса; он служит только для установки нового адреса в качестве основного.

Параметр сокета SCTP_STATUS

Этот параметр сокета служит для получения информации о текущем статусе ассоциации SCTP. Для обеспечения максимальной переносимости пользуйтесь функцией sctp_opt_info, а не getaddrinfo. Приложение должно предоставить структуру sctp_status, указав идентификатор ассоциации sstat_assoc_id. Структура будет заполнена информацией о выбранной ассоциации и возвращена приложению. Формат структуры sctp_statusтаков:

struct sctp_status {

 sctp_assoc_t sstat_assoc_id;

 int32_t sstat_state;

 u_int32_t sstat_rwnd;

 u_int16_t sstat_unackdata;

 u_int16_t sstat_penddata;

 u_int16_t sstat_instrms;

 u_int16_t sstat_outstrms;

 u_int32_t sstat_fragmentation_point;

 struct sctp_paddrinfo sstat_primary;

};

Поля структуры имеют следующий смысл:

■  sstat_assoc_idсодержит идентификатор ассоциации;

■  sstat_stateсодержит константу, обозначающую состояние ассоциации (табл. 7.8). Подробное описание состояний конечной точки SCTP, чередующихся при установке и завершении ассоциации, приводится на рис. 2.8;

■  sstat_rwndсодержит текущее вычисленное значение приемного окна собеседника;

■  sstat_unackdataсодержит количество неподтвержденных порций данных, ждущих ответа собеседника;

■  sstat_penddataсодержит количество непрочитанных порций данных, подготовленных локальной конечной точкой SCTP для приложения;

■  sstat_instrmsсодержит количество потоков, используемых собеседником для передачи данных на данную конечную точку;

■  sstat_outstrmsсодержит количество потоков, по которым данная конечная точка может передавать данные собеседнику;

■  sstat_fragmentation_pointсодержит текущее значение границы фрагментации пользовательских сообщений, используемое локальной конечной точкой SCTP. Это значение обычно равняется минимальной MTU для всех адресатов или еще меньшей величине, установленной при помощи параметра SCTP_MAXSEG;

■  sstat_primaryсодержит текущий основной адрес. Основной адрес используется по умолчанию для отправки данных собеседнику.

Таблица 7.8. Состояния SCTP


SCTP_CLOSEDАссоциация закрыта
SCTP_COOKIE_WAITАссоциация отправила пакет INIT
SCTP_COOKIE_ECHOEDАссоциация отправила эхо-ответ cookie
SCTP_ESTABLISHEDАссоциация установлена
SCTP_SHUTDOWN_PENDINGАссоциация ждет отправки сообщения о завершении
SCTP_SHUTDOWN_SENTАссоциация отправила сообщение о завершении
SCTP_SHUTDOWN_RECEIVEDАссоциация получила сообщение о завершении
SCTP_SHUTDOWN_ACK_SENTАссоциация ждет пакета SHUTDOWN-COMPLETE

Эти параметры полезны для диагностики соединения и определения характеристик текущего сеанса. Например, функция sctp_get_no_strmsв разделе 10.2 будет считывать sstat_outstrmsдля определения количества доступных для отправки данных потоков. Низкое значение sstat_rwndили высокое значение sstat_unackdataпозволяет сделать вывод о заполнении приемного буфера собеседника, так что приложение может вовремя замедлить передачу данных. Поле sstat_fragmentation_pointможет использоваться некоторыми приложениями для уменьшения количества пакетов, создаваемых SCTP, путем уменьшения размеров сообщений.

7.11. Функция fcntl

Сокращение fcntlозначает «управление файлами» (file control). Эта функция выполняет различные операции управления дескрипторами. Перед описанием этой функции и ее влияния на сокет нам нужно составить некоторое более общее представление о ее возможностях. В табл. 7.9 приводятся различные операции, выполняемые функциями fcntlи ioctlи маршрутизирующими сокетами.

Таблица 7.9. Операции функций fcntl и ioctl и маршрутизирующих сокетов


Установка сокета для неблокируемого ввода-выводаF_SETFL, O_NONBLOCKFIONBIOfcntl
Установка сокета для ввода-вывода, управляемого сигналомF_SETFL, O_ASYNCFIOASYNCfcntl
Установка владельца сокетаF_SETOWNSIOCSPGRP или FIOSETOWNfcntl
Получение владельца сокетаF_GETOWNSIOCGPGRP или FIOGETOWNfcntl
Получение текущего количества байтов в приемном буфере сокетаFIONREAD
Проверка, находится ли процесс на отметке внеполосных данныхSIOCATMARKsockatmark
Получение списка интерфейсовSIOCGIFCONFSysctl
Операции интерфейсовSIOC[GS]IF xxx
Кэш-операции ARPSIOC xARPRTM_ xxx
Операции таблицы маршрутизацииSIOG xxxRTRTM_ xxx

Первые шесть операций могут применяться к сокетам любым процессом, следующие две (операции над интерфейсами) используются реже, а последние две (ARP и таблица маршрутизации) выполняются администрирующими программами, такими как ifconfigи route. О различных операциях функции ioctlмы поговорим подробнее в главе 17, а о маршрутизирующих сокетах – в главе 18.

Существует множество способов выполнения первых четырех операций, но, как указано в последней колонке, стандарт POSIX определяет, что функция fcntlявляется предпочтительным способом. Отметим также, что POSIX предлагает функцию sockatmark(см. раздел 24.3) как наиболее предпочтительный способ тестирования на предмет пребывания процесса на отметке внеполосных данных. Оставшиеся операции с пустой последней колонкой не стандартизованы POSIX.

ПРИМЕЧАНИЕ

Отметим также, что первые две операции, устанавливающие сокет для неблокируемого ввода-вывода и для ввода-вывода, управляемого сигналом, традиционно применялись с использованием команд FNDELAY и FASYNC функции fcntl. POSIX определяет константы О_ xxx.

Функция fcntlпредоставляет следующие возможности, относящиеся к сетевому программированию:

■ Неблокируемый ввод-вывод. Мы можем установить флаг состояния файла O_NONBLOCK, используя команду F_SETFLдля отключения блокировки сокета. Неблокируемый ввод-вывод мы описываем в главе 16.

■ Управляемый сигналом ввод-вывод. Мы можем установить флаг состояния файла O_ASYNC, используя команду F_SETFL, после чего при изменении состояния сокета будет генерироваться сигнал SIGIO. Мы рассмотрим это в главе 25.

■ Команда F_SETOWNпозволяет нам установить владельца сокета (идентификатор процесса или идентификатор группы процессов), который будет получать сигналы SIGIOи SIGURG. Первый сигнал генерируется, если для сокета включен управляемый сигналом ввод-вывод (см. главу 25), второй – когда для сокета приходят новые внеполосные (out-of-band data) данные (см. главу 24). Команда F_GETOWNвозвращает текущего владельца сокета.

ПРИМЕЧАНИЕ

Термин «владелец сокета» определяется POSIX. Исторически реализации, происходящие от Беркли, называли его «идентификатор группы процессов сокета», потому что переменная, хранящая этот идентификатор, – это элемент so_pgid структуры socket [128, с. 438].

#include

int fcntl(int fd, int cmd, ... /* int arg */);

Возвращает: в случае успешного выполнения результат зависит от аргумента cmd, -1 в случае ошибки

Каждый дескриптор (включая сокет) имеет набор флагов, которые можно получить с помощью команды F_GETFLи установить с помощью команды F_SETFL. На сокет влияют следующие два флага:

■  O_NONBLOCK– неблокируемый ввод-вывод;

■  O_ASYNC– ввод-вывод, управляемый сигналом.

Позже мы опишем оба эти флага подробнее. Отметим, что типичный код, который устанавливает неблокируемый ввод-вывод с использованием функции fcntl, выглядит следующим образом:

int flags;

/* Делаем сокет неблокируемым */

if ((flags = fcntl(fd, F_GETFL, 0)) < 0)

 err_sys("F_GETFL error");

flags |= O_NONBLOCK;

if (fcntl(fd, F_SETFL, flags) < 0)

 err_sys("F_SETFL error");

Учтите, что вам может встретиться код, который просто устанавливает желаемый флаг:

/* Неправильный способ сделать сокет неблокируемым */

if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)

 err_sys("F_SETFL error");

Хотя при этом и устанавливается флаг отключения блокировки, также снимаются все остальные флаги состояния файла. Единственный корректный способ установить один из этих флагов состояния файла – получить текущие флаги, с помощью операции логического ИЛИ добавить новый флаг, а затем установить флаги.

Следующий код сбрасывает флаг отключения блокировки в предположении, что переменная flagsбыла задана с помощью вызова функции fcntl, показанного ранее:

flags &= ~O_NONBLOCK;

if (fcntl(fd, F_SETFL, flags) < 0)

 err_sys("F_SETFL error");

Сигналы SIGIOи SIGURGотличаются от других тем, что они генерируются для сокета, только если сокету был присвоен владелец с помощью команды F_SETOWN. Целое значение аргумента argдля команды F_SETOWNможет быть либо положительным, задающим идентификатор процесса, получающего сигнал, либо отрицательным, абсолютное значение которого – это идентификатор группы процессов, получающей сигнал. Команда F_GETOWNвозвращает владельца сокета, так как возвращаемое значение функции fcntl– либо идентификатор процесса (положительное возвращаемое значение), либо идентификатор группы процессов (отрицательное значение, отличное от -1). Разница между заданием процесса и группы процессов, получающих сигнал, в том, что в первом случае сигнал будет получен только одиночным процессом, тогда как во втором случае его получают все процессы в группе.

Когда создается новый сокет с помощью функции socket, у него нет владельца. Сокет, создаваемый из прослушиваемого сокета, наследует от него принадлежность владельцу (как и многие другие параметры сокетов [128, с. 462-463].

7.12. Резюме

Параметры сокетов лежат в широком диапазоне от очень общих ( SO_ERROR) до очень специфических (параметры заголовка IP). Наиболее общеупотребительные параметры сокетов, которые нам могут встретиться, – это SO_KEEPALIVE, SO_RCVBUF, SO_SNDBUFи SO_REUSEADDR. Последний должен всегда задаваться для сервера TCP до того, как сервер вызовет функцию bind(см. листинг 11.6). Параметр SO_BROADCASTи десять параметров сокетов многоадресной передачи предназначены только для приложений, передающих соответственно широковещательные или многоадресные сообщения.

Параметр сокета SO_KEEPALIVEустанавливается многими серверами TCP и автоматически закрывает наполовину открытое соединение. Замечательное свойство этого параметра в том, что он обрабатывается на уровне TCP, не требуя на уровне приложения наличия таймера, измеряющего период отсутствия активности. Однако недостаток этого параметра в том, что он не видит разницы между выходом собеседника из строя и временной потерей соединения с ним. SCTP предоставляет 17 параметров сокетов, с помощью которых приложение может управлять транспортным уровнем. SCTP_NODELAYи SCTP_MAXSEGаналогичны TCP_NODELAYи TCP_MAXSEG, и выполняют схожие функции. Остальные 17 параметров позволяют приложению более точно контролировать поведение стека SCTP. Большинство этих параметров будет рассмотрено в главе 23.

Параметр сокета SO_LINGERрасширяет наши возможности в отношении контроля над функцией close – мы можем отложить ее завершение на некоторое время. Кроме того, этот параметр позволяет нам отправить сегмент RST вместо обычной последовательности из четырех пакетов, завершающих соединение TCP. Следует соблюдать осторожность при отправке сегментов RST, поскольку в этом случае не наступает состояние TCP TIME_WAIT. Бывает, что этот параметр сокета не обеспечивает необходимой нам информации, и тогда требуется реализовать подтверждение на уровне приложения.

У каждого сокета TCP имеется буфер отправки и буфер приема, а у каждого сокета UDP есть буфер приема. Параметры сокета SO_SNDBUFи SO_RCVBUFпозволяют нам изменять размеры этих буферов. Основное применение эти функции находят при передаче большого количества данных по каналам с повышенной пропускной способностью, которые представляют собой соединения TCP либо с широкой полосой пропускания, либо с большой задержкой, часто с использованием расширений из RFC 1323. Сокеты UDP, наоборот, могут стремиться увеличить размер приемного буфера, чтобы позволить ядру установить в очередь больше дейтаграмм, если приложение занято.

Упражнения

1. Напишите программу, которая выводит заданные по умолчанию размеры буферов отправки и приема TCP, UDP и SCTP, и запустите ее в системе, к которой у вас имеется доступ.

2. Измените листинг 1.1 следующим образом. Перед вызовом функции connect вызовите функцию getsockopt, чтобы получить размер приемного буфера сокета и MSS. Выведите оба значения. После успешного завершения функции извлеките значения тех же двух параметров сокета и выведите их. Изменились ли значения? Почему? Запустите программу, соединяющуюся с сервером в вашей локальной сети, и программу, соединяющуюся с сервером в удаленной сети. Изменяется ли MSS? Почему? Запустите также программу на разных узлах, к которым у вас есть доступ.

3. Запустите наш сервер TCP, приведенный в листингах 5.1 и 5.2, и наш клиент из листингов 5.3 и 5.4. Измените функцию mainклиента, чтобы установить параметр сокета SO_LINGERперед вызовом функции exit, задав l_onoffравным 1, а l_linger – равным 0. Запустите сервер, а затем запустите клиент. Введите строку или две на стороне клиента для проверки работоспособности, а затем завершите работу клиента, введя символ конца файла. Что происходит? После завершения работы клиента запустите программу netstatна узле клиента и посмотрите, проходит ли сокет через состояние TIME_WAIT.

4. Будем считать, что два клиента TCP запускаются одновременно. Оба устанавливают параметр сокета SO_REUSEADDRи затем с помощью функции bindсвязываются с одним и тем же локальным IP-адресом и одним и тем же локальным портом (допустим, 1500). Но один из клиентов соединяется с помощью функции connect с адресом 198.69.10.2, порт 7000, а второй – с адресом 198.69.10.2 (тот же IP-адрес собеседника), порт 8000. Опишите возникающую ситуацию гонок.

5. Получите исходный код для примеров в этой книге (см. предисловие) и откомпилируйте программу sock (см. раздел В.3). Сначала классифицируйте свой узел как узел, не поддерживающий многоадресную передачу, затем – как поддерживающий многоадресную передачу, но не поддерживающий параметр SO_REUSEPORT, и наконец, как узел, поддерживающий многоадресную передачу с предоставлением параметра SO_REUSEPORT. Попытайтесь запустить несколько экземпляров программы sock в качестве сервера TCP (параметр -sкомандной строки) на одном и том же порте, связывая универсальный адрес, один из адресов интерфейсов вашего узла и адрес закольцовки (loopback address). Нужно ли вам задавать параметр SO_REUSEADDR(параметр командной строки)? Используйте программу netstatдля просмотра прослушиваемых сокетов.

6. Продолжайте предыдущий пример, но запустите сервер UDP (параметр -uкомандной строки) и попытайтесь запустить два экземпляра, связанные с одними и теми же локальным IP-адресом и портом. Если ваша реализация поддерживает параметр SO_REUSEPORT, попытайтесь использовать ее (параметр -Tкомандной строки).

7. Многие версии утилиты pingимеют флаг -d, задающий параметр сокета SO_DEBUG. В чем его назначение?

8. Продолжая пример в конце нашего обсуждения параметра сокета TCP_NODELAY, предположим, что клиент выполняет две операции записи с помощью функции write: первую для 4 байт данных и вторую для 396 байт. Также будем считать, что время задержки ACK – 100 мс, период RTT между клиентом и сервером равен 100 мс, а время обработки сервером каждого клиентского запроса – 50 мс. Нарисуйте временную диаграмму, показывающую взаимодействие алгоритма Нагла с задержанными сегментами ACK.

9. Снова выполните предыдущее упражнение, считая, что установлен параметр сокета TCP_NODELAY.

10. Снова выполните упражнение 8, считая, что процесс вызывает функцию writevодин раз для обоих буферов (4-байтового и 396-байтового).

11. Прочтите RFC 1122 [10], чтобы определить рекомендуемый интервал для задержанных сегментов ACK.

12. В какой из версий наш сервер тратит больше времени – в листинге 5.1 или 5.2? Что происходит, если сервер устанавливает параметр сокета SO_KEEPALIVE, через соединение не происходит обмена данными, узел клиента выходит из строя и не перезагружается?

13. В какой из версий наш клиент тратит больше времени – в листинге 5.3 или 5.4? Что происходит, если клиент устанавливает параметр сокета SO_KEEPALIVE, через соединение не происходит обмена данными и узел сервера выходит из строя и не перезагружается?

14. В какой из версий наш клиент тратит больше времени – в листинге 5.3 или 6.2? Что происходит, если клиент устанавливает параметр сокета SO_KEEPALIVE, через соединение не происходит обмена данными и узел сервера выходит из строя и не перезагружается?

15. Будем считать, что и клиент, и сервер устанавливают параметр сокета SO_KEEPALIVE. Между собеседниками поддерживается соединение, но через это соединение не происходит обмена данными между приложениями. Когда проходят условленные 2 ч и требуется проверить наличие связи, сколькими сегментами TCP обмениваются собеседники?

16. Почти все реализации определяют константу SO_ACCEPTCONNв заголовочном файле , но мы не описывали этот параметр. Прочтите [69], чтобы понять, зачем этот параметр существует.

Глава 8
Основные сведения о сокетах UDP
8.1. Введение

Приложения, использующие TCP и UDP, фундаментально отличаются друг от друга, потому что UDP является ненадежным протоколом дейтаграмм, не ориентированным на установление соединения, и этим принципиально непохож на ориентированный на установление соединения и надежную передачу потока байтов TCP. Тем не менее есть случаи, когда имеет смысл использовать UDP вместо TCP. Подобные случаи мы рассматриваем в разделе 22.4. Некоторые популярные приложения построены с использованием UDP, например DNS (Domain Name System – система доменных имен), NFS (сетевая файловая система – Network File System) и SNMP (Simple Network Management Protocol – простой протокол управления сетью).

На рис. 8.1 показаны вызовы функций для типичной схемы клиент-сервер UDP. Клиент не устанавливает соединения с сервером. Вместо этого клиент лишь отправляет серверу дейтаграмму, используя функцию sendto(она описывается в следующем разделе), которой нужно задать адрес получателя (сервера) в качестве аргумента. Аналогично, сервер не устанавливает соединения с клиентом. Вместо этого сервер лишь вызывает функцию recvfrom, которая ждет, когда придут данные от какого-либо клиента. Функция recvfromвозвращает адрес клиента (для данного протокола) вместе с дейтаграммой, и таким образом сервер может отправить ответ именно тому клиенту, который прислал дейтаграмму.

Рис. 8.1. Функции сокета для модели клиент-сервер UDP

Рисунок 8.1 иллюстрирует временную диаграмму типичного сценария обмена UDP-дейтаграммами между клиентом и сервером. Мы можем сравнить этот пример с типичным обменом по протоколу TCP, изображенным на рис. 4.1.

В этой главе мы опишем новые функции, применяемые с сокетами UDP, – recvfromи sendto, и переделаем нашу модель клиент-сервер для применения UDP. Кроме того, мы рассмотрим использование функции connect с сокетом UDP и концепцию асинхронных ошибок.


    Ваша оценка произведения:

Популярные книги за неделю