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

ЖАНРЫ

UNIX: разработка сетевых приложений
Шрифт:
Protocol Значение
IPPROTO_TCP Транспортный протокол TCP
IPPROTO_UDP Транспортный протокол UDP
IPPROTO_SCTP Транспортный протокол SCTP

Не все сочетания констант

family
и
type
допустимы. В табл. 4.4 показаны допустимые сочетания, а также протокол, соответствующий каждой паре. Клетки таблицы, содержащие «Да», соответствуют допустимым комбинациям, для которых нет удобных сокращений. Пустая клетка означает, что данное сочетание не поддерживается.

Таблица 4.4. Сочетания констант family и type для функции socket

AF_INET AF_INET6 AF_LOCAL AF_ROUTE AF_KEY
SOCK_STREAM TCP/SCTP TCP/SCTP Да
SOCK_DGRAM UDP UDP Да
SOCK_SEQPACKET SCTP SCTP Да
SOCK RAW IPv4 IPv6 Да Да
ПРИМЕЧАНИЕ

В

качестве первого аргумента функции socket вы также можете встретить константу PF_xxx. Подробнее об этом мы расскажем в конце данного раздела.

Кроме того, вам может встретиться название AF_UNIX (исторически сложившееся в Unix) вместо AF_LOCAL (название из POSIX), и более подробно мы поговорим об этом в главе 14.

Для аргументов family и type существуют и другие значения. Например, 4.4BSD поддерживает и AF_NS (протоколы Xerox NS, часто называемые XNS), и AF_ISO (протоколы OSI). Но сегодня очень немногие используют какой-либо из этих протоколов. Аналогично, значение type для SOCK_SEQPACKET, сокета последовательных пакетов, реализуется и протоколами Xerox NS, и протоколами OSI. Но протокол TCP является потоковым и поддерживает только сокеты SOCK_STREAM.

Linux поддерживает новый тип сокетов, SOCK_PACKET, предоставляющий доступ к канальному уровню, аналогично BPF и DLPI на рис. 2.1. Об этом более подробно рассказывается в главе 29.

Сокет управления ключами AF_KEY является новшеством. Аналогично тому, как маршрутизирующий сокет (AF_ROUTE) является интерфейсом к таблице маршрутизации ядра, сокет управления ключами — это интерфейс к таблице ключей ядра. Подробнее об этом рассказывается в главе 19.

При успешном выполнении функция

socket
возвращает неотрицательное целое число, аналогичное дескриптору файла. Мы называем это число дескриптором сокета( socket descriptor), или
sockfd
. Чтобы получить дескриптор сокета, достаточно указать лишь семейство протоколов (IPv4, IPv6 или Unix) и тип сокета (потоковый, символьный или дейтаграммный). Мы еще не задали ни локальный адрес протокола, ни удаленный адрес протокола.

AF_xxx и PF_xxx

Префикс

AF_
обозначает семейство адресов( address family), a
PF_
семейство протоколов( protocol family). Исторически ставилась такая цель, чтобы отдельно взятое семейство протоколов могло поддерживать множество семейств адресов и значение
PF_
использовалось для создания сокета, а значение
AF_
— в структурах адресов сокетов. Но в действительности семейства протоколов, поддерживающего множество семейств адресов, никогда не существовало, и поэтому в заголовочном файле
<sys/socket.h>
значение
PF_
для протокола задается равным значению
AF_
. Хотя не гарантируется, что это равенство будет всегда справедливо, но при попытке изменить ситуацию для существующих протоколов большая часть написанного кода потеряет работоспособность.

ПРИМЕЧАНИЕ

Просмотр 137 программ с вызовами функции socket в реализации BSD/OS 2.1 показывает, что в 143 случаях вызова задается значение AF_, и только в 8 случаях — значение PF_.

Причина создания аналогичных наборов констант с префиксами AF_ и PF_ восходит к 4.1cBSD [69] и к версии функции socket, предшествующей описываемой нами версии (которая появилась с 4.2BSD). Версия функции socket в 4.1cBSD получала четыре аргумента, одним из которых был указатель на структуру sockproto. Первый элемент этой структуры назывался sp_family, и его значение было одним из значений PF_. Второй элемент, sp_protocol, был номером протокола, аналогично третьему аргументу нынешней функции socket. Единственный способ задать семейство протоколов заключался в том, чтобы задать эту структуру. Следовательно, в этой системе значения PF_ использовались как элементы для задания семейства протоколов в структуре sockproto. Значения AF_ играли роль элементов для задания семейства

адресов в структурах адресов сокетов. Структура sockproto еще присутствует в 4.4BSD [128, с. 626-627], но служит только для внутреннего использования ядром. Начальное определение содержало для элемента sp_family комментарий «семейство протоколов», но в исходном коде 4.4BSD он был изменен на «семейство адресов».

Еще большую путаницу в эту ситуацию вносит то, что в Беркли-реализации структура данных ядра, содержащая значение, которое сравнивается с первым аргументом функции socket (элемент dom_family структуры domain [128, с. 187]), сопровождается комментарием, где сказано, что в этой структуре содержится значение AF_. Но некоторые структуры domain внутри ядра инициализированы с помощью константы AF_ [128, с. 192], в то время как другие — с помощью PF_ [128, с. 646], [112, с. 229].

Еще одно историческое замечание. Страница руководства по 4.2BSD от июля 1983 года, посвященная функции socket, называет ее первый аргумент af и перечисляет его возможные значения как константы AF_.

Наконец, отметим, что POSIX задает первый аргумент функции socket как значение PF_, а значение AF_ использует для структуры адреса сокета. Но далее в структуре addrinfo определяется только одно значение семейства (см. раздел 11.2), предназначенное для использования либо в вызове функции socket, либо в структуре адреса сокета!

В целях согласования с существующей практикой программирования мы используем в тексте только константы

AF_
, хотя вы можете встретить и значение
PF_
, в основном в вызовах функции
socket
.

4.3. Функция connect

Функция

connect
используется клиентом TCP для установления соединения с сервером TCP.

#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr * servaddr,

socklen_t addrlen);

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

Аргумент

sockfd
— это дескриптор сокета, возвращенный функцией
socket
. Второй и третий аргументы — это указатель на структуру адреса сокета и ее размер (см. раздел 3.3). Структура адреса сокета должна содержать IP-адрес и номер порта сервера. Пример применения этой функции был представлен в листинге 1.1.

Клиенту нет необходимости вызывать функцию

bind
(которую мы описываем в следующем разделе) до вызова функции
connect
: при необходимости ядро само выберет и динамически назначаемый порт, и IP-адрес отправителя.

В случае сокета TCP функция

connect
инициирует трехэтапное рукопожатие TCP (см. раздел 2.6). Функция возвращает значение, только если установлено соединение или произошла ошибка. Возможно несколько ошибок:

1. Если клиент TCP не получает ответа на свой сегмент SYN, возвращается сообщение

ETIMEDOUT
. 4.4BSD, например, отправляет один сегмент SYN, когда вызывается функция
connect
, второй — 6 с спустя, и еще один — через 24 с [128, с. 828]. Если ответ не получен в течение 75 с, возвращается ошибка.

Некоторые системы позволяют администратору устанавливать значение времени ожидания; см. приложение Е [111].

2. Если на сегмент SYN сервер отвечает сегментом RST, это означает, что ни один процесс на узле сервера не находится в ожидании подключения к указанному нами порту (например, нужный процесс может быть не запущен). Это устойчивая неисправность( hard error), и клиенту возвращается сообщение

ECONNREFUSED
сразу же по получении им сегмента RST.

RST (от «reset» — сброс) — это сегмент TCP, отправляемый собеседнику при возникновении ошибок. Вот три условия, при которых генерируется RST: сегмент SYN приходит для порта, не имеющего прослушивающего сервера (что мы только что описали); TCP хочет разорвать существующее соединение; TCP получает сегмент для несуществующего соединения (дополнительная информация содержится на с. 246–250 [111]).

3. Если сегмент SYN клиента приводит к получению сообщения ICMP о недоступности получателя от какого-либо промежуточного маршрутизатора, это считается случайным сбоем( soft error). Клиентское ядро сохраняет сообщение об ошибке, но продолжает отправлять сегменты SYN с теми же временными интервалами, что и в первом сценарии. Если же но истечении определенного фиксированного времени (75 с для 4.4BSD) ответ не получен, сохраненная ошибка ICMP возвращается процессу либо как

EHOSTUNREACH
, либо как
ENETUNREACH
. Может случиться, что удаленная система будет недоступна по любому маршруту из таблицы маршрутизации локального узла, или что возврат из
connect
произойдет без всякого ожидания.

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