Читаем Linux API. Исчерпывающее руководство полностью

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

В обоих случаях оставшийся конец соединения не может принимать новые запросы. Хотя большинство реализаций протокола TCP не дали бы новому слушающему сокету привязаться к уже занятому серверному порту.

Ошибка EADDRINUSE обычно не встречается на клиентской стороне, где, как правило, используются динамические порты, которые никогда не совпадают с теми, что уже заняты соединением в состоянии TIME_WAIT. Однако клиенты, привязанные к порту с определенным номером, не защищены от этой ошибки.

Чтобы понять принцип работы параметра SO_REUSEADDR, вернемся к нашей аналогии с телефонами, которую мы приводили ранее в разделе 52.5 при знакомстве с потоковыми сокетами. Как и любой телефонный вызов (кроме, разве что, телеконференций), TCP-соединение идентифицируется с помощью сочетания из двух конечных точек, подключенных друг к другу. Вызов accept() аналогичен процедуре, выполняемой внутренним коммутатором («сервером»). Обнаружив внешний вызов, коммутатор направляет его к какому-то телефону внутри организации («новому сокету»). Внешний наблюдатель не имеет возможности определить внутренний телефон. Единственный способ различить несколько звонков, выполненных извне, — использовать комбинацию внешнего номера вызывающего абонента и номера коммутатора (последний необходим, поскольку таких коммутаторов в телефонной сети может быть сколько угодно). Аналогично мы создаем новый сокет каждый раз, когда принимаем соединение с помощью слушающего сокета. Все эти сокеты (включая слушающий) связаны с одним и тем же локальным адресом. Различить их можно только по тому, с какими удаленными сокетами они соединены.

Иными словами, подключенный TCP-сокет идентифицируется путем комбинации из четырех значений следующего вида:

{ локальный-IP-адрес, локальный-порт, удаленный-IP-адрес, удаленный-порт }

Спецификация протокола TCP требует, чтобы каждый такой набор был уникальным; то есть у соответствующего соединения может быть только один экземпляр («телефонный звонок»). Проблема вот в чем: в большинстве реализаций (включая Linux) действует более строгое ограничение: локальный порт нельзя использовать повторно (передавая его вызову bind()), если в системе существует экземпляр TCP-соединения с тем же портом. Это правило действует даже в том случае, если сокет не принимает новые соединения (как в сценарии, описанном в начале данного раздела).

Применение параметра SO_REUSEADDR смягчает указанное ограничение, делая его более близким к требованиям протокола TCP. По умолчанию этот параметр равен 0 (то есть выключен). Чтобы его включить, перед привязкой сокета ему нужно установить ненулевое значение, как показано в листинге 57.4.

Установка параметра SO_REUSEADDR позволяет привязывать сокет к локальному порту, даже если он занят другим TCP-соединением (в любом из двух сценариев, описанных в начале этого раздела). Данный параметр следует включать в большинстве TCP-серверов. Мы уже сталкивались с его применением в листингах 55.6 и 55.9.

Листинг 57.4. Установка параметра сокета SO_REUSEADDR

int sockfd, optval;

sockfd = socket(AF_INET, SOCK_STREAM, 0);

if (sockfd == -1)

errExit("socket");

optval = 1;

if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval,

sizeof(optval)) == -1)

errExit("socket");

if (bind(sockfd, &addr, addrlen) == -1)

errExit("bind");

if (listen(sockfd, backlog) == -1)

errExit("listen");

57.11. Наследование флагов и параметров сокета при выполнении вызова accept()

С дескрипторами и описаниями открытого файла могут быть связаны различные флаги и настройки (см. раздел 5.4). Кроме того, как было показано в разделе 57.9, сокету можно устанавливать разные параметры. Если речь идет о слушающем сокете, то наследуются ли все эти метаданные новым сокетом, который возвращается вызовом accept()? В данном разделе мы дадим подробный ответ.

В Linux новый файловый дескриптор, полученный из вызова accept(), не наследует следующие атрибуты.

• Флаги состояния, связанные с описанием открытого файла, например O_NONBLOCK или O_ASYNC. Для их включения или выключения можно использовать операцию F_SETFL вызова fcntl() (см. раздел 5.3).

• Флаги файлового дескриптора. В эту категорию входит лишь один флаг, FD_CLOEXEC (описанный в разделе 27.4), и для его изменения тоже можно применять операцию F_SETFL вызова fcntl().

• Атрибуты файлового дескриптора F_SETOWN (идентификатор процесса-владельца) и F_SETSIG (сгенерированный сигнал), связанные с вводом/выводом на основе сигналов (см. раздел 59.3).

С другой стороны, новый дескриптор, возвращаемый вызовом accept(), наследует копии большинства параметров сокета, которые можно установить с помощью setsockopt() (см. раздел 57.9).

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных