Исключением из этого правила являются серверы удаленного вызова процедур RPC (Remote Procedure Call). Обычно они позволяют ядру выбирать динамически назначаемый порт для их прослушиваемого сокета, поскольку затем этот порт регистрируется программой отображения портов RPC. Клиенты должны соединиться с этой программой, чтобы получить номер динамически назначаемого порта до того, как они смогут соединиться с сервером с помощью функции connect. Это также относится к серверам RPC, использующим протокол UDP.
С помощью функции
bind
процесс может связать конкретный IP-адрес с сокетом. IP-адрес должен соответствовать одному из интерфейсов узла. Так определяется IP-адрес, который будет использоваться для отправляемых через сокет IP-дейтаграмм. При этом для сервера TCP на сокет накладывается ограничение: он может принимать только такие входящие соединения клиента, которые предназначены именно для этого IP-адреса.
Обычно клиент TCP не связывает IP-адрес с сокетом при помощи функции
bind
. Ядро выбирает IP-адрес отправителя в момент подключения клиента к сокету, основываясь на используемом исходящем интерфейсе, который, в свою очередь, зависит от маршрута, требуемого для обращения к серверу [128, с. 737].
Если сервер TCP не связывает IP-адрес с сокетом, ядро назначает ему IP-адрес (указываемый в исходящих пакетах), который совпадает с адресом получателя сегмента SYN клиента [128, с. 943].
Как мы уже говорили, вызов функции
bind
позволяет нам задать IP-адрес и порт (вместе или по отдельности) либо не задавать никаких аргументов. В табл. 4.5 приведены все возможные значения, которые присваиваются аргументам
sin_addr
и
sin_port
либо
sin6_addr
и
sin6_port
в зависимости от желаемого результата.
Таблица 4.5. Результаты задания IP-адреса и (или) номера порта в функции bind
Процесс задает | Результат | |
---|---|---|
IP-адрес | Порт | |
Универсальный | 0 | Ядро выбирает IP-адрес и порт |
Универсальный | Ненулевое значение | Ядро выбирает IP-адрес, процесс задает порт |
Локальный | 0 | Процесс задает IP-адрес, ядро выбирает порт |
Локальный | Ненулевое значение | Процесс задает IP-адрес и порт |
Если мы зададим нулевой номер порта, то при вызове функции
bind
ядро выберет динамически назначаемый порт. Но если мы зададим IP-адрес с помощью символов подстановки, ядро не выберет локальный IP-адрес, пока к сокету не присоединится клиент (TCP) либо на сокет не будет отправлена дейтаграмма (UDP).
В случае IPv4
INADDR_ANY
, значение которой обычно нулевое. Это указывает ядру на необходимость выбора IP-адреса. Пример вы видели в листинге 1.5:
struct sockaddr_in servaddr;
servaddr sin_addr s_addr = htonl(INADDR_ANY); /* универсальный */
Этот прием работает с IPv4, где IP-адрес является 32-разрядным значением, которое можно представить как простую численную константу (в данном случае 0), но воспользоваться им при работе с IPv6 мы не можем, поскольку 128-разрядный адрес IPv6 хранится в структуре. (В языке С мы не можем поместить структуру в правой части оператора присваивания.) Эта проблема решается следующим образом:
struct sockaddr_in6 serv;
serv sin6_addr = in6addr_any; /* универсальный */
Система выделяет место в памяти и инициализирует переменную
in6addr_any
, присваивая ей значение константы
IN6ADDR_ANY_INIT
. Объявление внешней константы
in6addr_any
содержится в заголовочном файле
.
Значение
INADDR_ANY
(0) не зависит от порядка байтов, поэтому использование функции
htonl
в действительности не требуется. Но поскольку все константы
INADDR_
, определенные в заголовочном файле
, задаются в порядке байтов узла, с любой из этих констант следует использовать функцию
htonl
.