Параметры сокета SO_REUSEADDR и SO_REUSEPORT

Параметры сокета SO_REUSEADDR и SO_REUSEPORT

Параметр сокета SO_REUSEADDR служит для четырех целей.

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

 1) запускается прослушивающий сервер;

 2) от клиента приходит запрос на соединение, и для обработки этого клиента генерируется дочерний процесс;

 3) прослушивающий сервер завершает работу, но дочерний процесс продолжает обслуживание клиента на существующем соединении;

 4) прослушивающий сервер перезапускается.

По умолчанию, когда прослушивающий сервер перезапускается при помощи вызова функций socket, bind и listen, вызов функции bind оказывается неудачным, потому что прослушивающий сервер пытается связаться с портом, который является частью существующего соединения (обрабатываемого ранее созданным дочерним процессом). Но если сервер устанавливает параметр сокета SO_REUSEADDR между вызовами функций socket и bind, последняя выполнится успешно. Все серверы TCP должны задавать этот параметр сокета, чтобы позволить перезапускать сервер в подобной ситуации.

ПРИМЕЧАНИЕ

Этот сценарий вызывает больше всего вопросов в Usenet.

2. Параметр SO_REUSEADDR позволяет множеству экземпляров одного и того же сервера запускаться на одном и том же порте, если все экземпляры связываются с различными локальными IP-адресами. Это типичная ситуация для узла, на котором размещаются несколько серверов HTTP, использующих технологию альтернативных IP-адресов, или псевдонимов (IP alias technique) (см. раздел А.4). Допустим, первичный IP-адрес локального узла — 198.69.10.2, но он имеет два альтернативных адреса — 198.69.10.128 и 198.69.10.129. Запускаются три сервера HTTP. Первый сервер с помощью функции bind свяжется с локальным IP-адресом 198.69.10.128 и локальным портом 80 (заранее известный порт HTTP). Второй сервер с помощью функции bind свяжется с локальным IP-адресом 198.69.10.129 и локальным портом 80. Но второй вызов функции bind не будет успешным, пока не будет установлен параметр SO_REUSEADDR перед обращением к ней. Третий сервер вызовет функцию bind с универсальным адресом в качестве локального IP-адреса и локальным портом 80. И снова требуется параметр SO_REUSEADDR, для того чтобы последний вызов оказался успешным. Если считать, что установлен параметр SO_REUSEADDR и запущены три сервера, то входящие запросы TCP на соединение с IP-адресом получателя 198.69.10.128 и портом получателя 80 доставляются на второй сервер, входящие запросы на соединение с IP-адресом получателя 198.69.10.129 и портом получателя 80 — на третий сервер, а все остальные входящие запросы TCP на соединение с портом получателя 80 доставляются на первый сервер. Этот сервер обрабатывает запросы, адресованные на 198.69.10.2, в дополнение к другим альтернативным IP-адресам, для которых этот узел может быть сконфигурирован. Символ подстановки означает в данном случае «все, для чего не нашлось более точного совпадения». Заметим, что этот сценарий, допускающий множество серверов для данной службы, обрабатывается автоматически, если сервер всегда устанавливает параметр сокета SO_REUSEADDR (как мы рекомендуем).

TCP не дает нам возможности запустить множество серверов, которые с помощью функции bind связываются с одним и тем же IP-адресом и одним и тем же портом: это случай полностью дублированного связывания (completely duplicate binding). То есть мы не можем запустить один сервер, связывающийся с адресом 198.69.10.2 и портом 80, и другой сервер, также связывающийся с адресом 198.69.10.2 и портом 80, даже если для второго сервера мы установим параметр SO_REUSEADDR.

По соображениям безопасности некоторые операционные системы запрещают связывать несколько серверов с адресом подстановки, то есть описанный выше сценарий не работает даже с использованием параметра SO_REUSEADDR. В такой системе сервер, связываемый с адресом подстановки, должен запускаться последним. Таким образом предотвращается привязка сервера злоумышленника к IP-адресу и порту, которые уже обрабатываются системной службой. Особенно это важно для службы NFS, которая обычно не использует выделенный порт.

3. Параметр SO_REUSEADDR позволяет одиночному процессу связывать один и тот же порт с множеством сокетов, так как при каждом связывании задается уникальный IP-адрес. Это обычное явление для серверов UDP, так как им необходимо знать IP-адрес получателя запросов клиента в системах, не поддерживающих параметр сокета IP_RECVSTADDR. Эта технология обычно не применяется с серверами TCP, поскольку сервер TCP всегда может определить IP-адрес получателя при помощи вызова функции getsockname, после того как соединение установлено. Однако на многоинтерфейсном узле сервер TCP, работающий с частью адресов локального узла, мог бы воспользоваться этой функцией.

4. Параметр SO_REUSEADDR допускает полностью дублированное связывание: связывание с помощью функции bind с IP-адресом и портом, когда тот же IP-адрес и тот же порт уже связаны с другим сокетом. Обычно это свойство доступно только в системах с поддержкой многоадресной передачи без поддержки параметра сокета SO_REUSEPORT (который мы опишем чуть ниже), и только для сокетов UDP (многоадресная передача не работает с TCP).

Это свойство применяется при многоадресной передаче для многократного выполнения одного и того же приложения на одном и том же узле. Когда приходит дейтаграмма UDP для одного из многократно связанных сокетов, действует следующее правило: если дейтаграмма предназначена либо для широковещательного адреса, либо для адреса многоадресной передачи, то одна копия дейтаграммы доставляется каждому сокету. Но если дейтаграмма предназначена для адреса направленной передачи, то дейтаграмма доставляется только на один сокет. Какой сокет получит дейтаграмму, если в случае направленной передачи существует множество сокетов, соответствующих дейтаграмме, — зависит от реализации. На с. 777-779 [128] об этом свойстве рассказывается более подробно. О широковещательной и многоадресной передаче мы поговорим соответственно в главах 20 и 21.

В упражнениях 7.5 и 7.6 показаны примеры использования этого параметра сокета.

Вместо того чтобы перегружать параметр SO_REUSEADDR семантикой многоадресной передачи, допускающей полностью дублированное связывание, в 4.4BSD был введен новый параметр сокета SO_REUSEPORT, обладающий следующей семантикой:

1. Этот параметр допускает полностью дублированное связывание, но только если каждый сокет, который хочет связаться с тем же IP-адресом и портом, задает этот параметр сокета.

2. Параметр SO_REUSEADDR считается эквивалентным параметру SO_REUSEPORT, если связываемый IP-адрес является адресом многоадресной передачи [128, с. 731].

Проблема с этим параметром сокета заключается в том, что не все системы его поддерживают. В системах без поддержки этого параметра, но с поддержкой многоадресной передачи его функции выполняет параметр SO_REUSEADDR, допускающий полностью дублированное связывание, когда оно имеет смысл (то есть когда имеется сервер UDP, который может быть запущен много раз на одном и том же узле в одно и то же время, предполагающий получать либо широковещательные дейтаграммы, либо дейтаграммы многоадресной передачи).

Обобщить обсуждение этих параметров сокета можно с помощью следующих рекомендаций:

1. Устанавливайте параметр SO_REUSEADDR перед вызовом функции bind на всех серверах TCP.

2. При создании приложения многоадресной передачи, которое может быть запущено несколько раз на одном и том же узле в одно и то же время, устанавливайте параметр SO_REUSEADDR и связывайтесь с адресом многоадресной передачи, используемым в качестве локального IP-адреса.

Более подробно об этих параметрах сокета рассказывается в главе 22 [128].

Существует потенциальная проблема безопасности, связанная с использованием параметра SO_REUSEADDR. Если существует сокет, связанный, скажем, с универсальным адресом и портом 5555, то, задав параметр SO_REUSEADDR, мы можем связать этот порт с другим IP-адресом, например с основным (primary) IP-адресом узла. Любые приходящие дейтаграммы, предназначенные для порта 5555 и IP- адреса, который мы связали с нашим сокетом, доставляются на наш сокет, а не на другой сокет, связанный с универсальным адресом. Это могут быть сегменты SYN TCP или дейтаграммы UDP. (В упражнении 11.9 показано это свойство для UDP.) Для большинства известных служб, таких как HTTP, FTP и Telnet, это не составляет проблемы, поскольку все эти серверы связываются с зарезервированным портом. Следовательно, любой процесс, запущенный позже и пытающийся связаться с конкретным экземпляром этого порта (то есть пытающийся завладеть портом), требует прав привилегированного пользователя. Однако NFS (Network File System — сетевая файловая система) может вызвать проблемы, поскольку ее стандартный порт (2049) не зарезервирован.

ПРИМЕЧАНИЕ

Одна из сопутствующих проблем API сокетов в том, что установка пары сокетов выполняется с помощью двух вызовов функций (bind и connect) вместо одного. В [122] предлагается одиночная функция, разрешающая эту проблему:

int bind_connect_listen(int sockfd,

 const struct sockaddr *laddr, int laddrlen,

 const struct sockaddr *faddr, int faddrlen,

 int listen);

Аргумент laddr задает локальный IP-адрес и локальный порт, аргумент faddr — удаленный IP-адрес и удаленный порт, аргумент listen задает клиент (0) или сервер (значение ненулевое; то же, что и аргумент backlog функции listen). В таком случае функция bind могла бы быть библиотечной функцией, вызывающей эту функцию с пустым указателем faddr и нулевым faddrlen, а функция connect — библиотечной функцией, вызывающей эту функцию с пустым указателем laddr и нулевым laddrlen. Существует несколько приложений, особенно FTP, которым необходимо задавать и локальную пару, и удаленную пару, которые могут вызывать bind_connect_listen непосредственно. При наличии подобной функции отпадает необходимость в параметре SO_REUSEADDR, в отличие от серверов UDP, которым явно необходимо допускать полностью дублированное связывание с одним и тем же IP-адресом и портом. Другое преимущество этой новой функции в том, что сервер TCP может ограничить себя обслуживанием запросов на соединения, приходящих от одного определенного IP-адреса и порта. Это определяется в RFC 793 [96], но невозможно с существующими API сокетов.

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

Создание сокета

Из книги Системное программирование в среде Windows автора Харт Джонсон М

Создание сокета Инициализировав Winsock DLL, вы можете использовать стандартные (Berkeley Sockets) функции для создания сокетов и соединений, обеспечивающих взаимодействие серверов с клиентами или взаимодействие равноправных узлов сети между собой.Используемый в Winsock тип данных


Связывание сокета

Из книги О чём не пишут в книгах по Delphi автора Григорьев А. Б.

Связывание сокета Следующий шаг заключается в привязке сокета к его адресу и конечной точке (endpoint) (направление канала связи от приложения к службе). Вызов socket, за которым следует вызов bind, аналогичен созданию именованного канала. Однако не существует имен, используя


2.1.8. Создание сокета

Из книги Разработка приложений в среде Linux. Второе издание автора Джонсон Майкл К.


2.1.17. Параметры сокета

Из книги C++ для начинающих автора Липпман Стенли

2.1.17. Параметры сокета Каждый сокет обладает рядом параметров (опций), которые влияют на его работу. Существуют параметры уровня сокета, которые относятся к сокету как к объекту безотносительно используемого протокола и его уровня. Впрочем, некоторые параметры уровня


17.3.1. Создание сокета

Из книги UNIX: разработка сетевых приложений автора Стивенс Уильям Ричард

17.3.1. Создание сокета Новые сокеты создаются системным вызовом socket(), который возвращает файловый дескриптор для неинициализированного сокета. При создании сокет привязывается к определенному протоколу, однако соединение для сокета не устанавливается. На данном этапе


17.6.1. Создание UDP-сокета

Из книги автора

17.6.1. Создание UDP-сокета Как и любой другой сокет, UDP-сокет создается с помощью функции socket(), однако второй аргумент должен быть SOCK_DGRAM, а последний — либо IPPROTO_UDP, либо просто ноль (так как UDP является стандартным IP-дейтаграммным протоколом).После создания сокета ему


7.3.2. Параметры-ссылки и параметры-указатели

Из книги автора

7.3.2. Параметры-ссылки и параметры-указатели Когда же лучше использовать параметры-ссылки, а когда – параметры-указатели? В конце концов, и те и другие позволяют функции модифицировать объекты, эффективно передавать в функцию большие объекты типа класса. Что выбрать:


Параметры сокета SO_RCVBUF и SO_SNDBUF

Из книги автора

Параметры сокета SO_RCVBUF и SO_SNDBUF У каждого сокета имеется буфер отправки и приемный буфер (буфер приема). Мы изобразили действие буферов отправки TCP, UDP и SCTP на рис. 2.15, 2.16 и 2.17.Приемные буферы используются в TCP, UDP и SCTP для хранения полученных данных, пока они не будут считаны


Параметры сокета SO_RCVLOWAT и SO_SNDLOWAT

Из книги автора

Параметры сокета SO_RCVLOWAT и SO_SNDLOWAT Каждый сокет характеризуется также минимальным количеством данных (low- water mark) для буферов приема и отправки. Эти значения используются функцией select, как мы показали в разделе 6.3. Указанные параметры сокета позволяют нам изменять эти два


Параметры сокета SO_RCVTIMEO и SO_SNDTIMEO

Из книги автора

Параметры сокета SO_RCVTIMEO и SO_SNDTIMEO Эти два параметра сокета позволяют нам устанавливать тайм-аут при получении и отправке через сокет. Обратите внимание, что аргумент двух функций sockopt — это указатель на структуру timeval, ту же, которую использует функция select (раздел 6.3). Это


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

Из книги автора

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


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

Из книги автора

Параметр сокета IPV6_NEXTHOP Этот параметр задает адрес следующего транзитного узла для дейтаграммы в виде структуры адреса сокета. Подробнее о нем рассказывается в разделе


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

Из книги автора

Параметр сокета IPV6_PATHMTU Этот параметр может быть только получен, но не установлен. При его считывании система возвращает текущее значение маршрутной MTU, определенное соответствующим методом (см. раздел


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

Из книги автора

Параметр сокета IPV6_RECVDSTOPTS Установка этого параметра означает, что любые полученные IPv6-параметры получателя должны быть возвращены в качестве вспомогательных данных функцией recvmsg. По умолчанию параметр отключен. Мы опишем функции, используемые для создания и обработки


Параметры сокета IPV6_XXX

Из книги автора

Параметры сокета IPV6_XXX Большинство параметров IPv6, предназначенных для изменения содержимого заголовка, предполагают, что приложение использует сокет UDP и взаимодействует с ядром при помощи функций recvmsg и sendmsg. Сокет TCP получает и устанавливает значения параметров при


27.5. Параметры транзитных узлов и параметры получателя IPv6

Из книги автора

27.5. Параметры транзитных узлов и параметры получателя IPv6 Параметры для транзитных узлов и параметры получателя IPv6 имеют одинаковый формат, показанный на рис. 27.3. Восьмиразрядное поле следующий заголовок (next header) идентифицирует следующий заголовок, который следует за