Вопрос

Можно ли отключить прослушивание сокета после вызова Listen(fd, backlog)?

Редактировать:Моя ошибка, что я не выразился ясно.Я хотел бы иметь возможность временно отключить прослушивание сокета.Вызов close() оставит сокет в состоянии M2LS и не позволит мне повторно открыть его (или, что еще хуже, какая-то гнусная программа может привязаться к этому сокету)

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

Это было полезно?

Решение

Некоторые библиотеки сокетов позволяют вам специально отклонять входящие соединения.Например: CommonC++ GNU:Класс TCPсокета имеет отклонять метод.

BSD Sockets не имеет этой функциональности.Ты можешь принимать соединение, а затем сразу закрывать его, оставив сокет открытым:

while (running) {

  int i32ConnectFD = accept(i32SocketFD, NULL, NULL);
  while (noConnectionsPlease) {
    shutdown(i32ConnectFD, 2);
    close(i32ConnectFD);
    break;
  }

}

Другие советы

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

Закрой его.Насколько я помню;

close(fd);

Судя по вашей отредактированной версии вопроса, я не уверен, что вам нужно «не слушать» или закрывать().На ум приходят два варианта:

1) После вызова метода Listen() соединения фактически не принимаются до тех пор, пока (достаточно логично) вы не вызовете метод Accept().Вы можете «не слушать», просто игнорируя активность сокетов и откладывая любые вызовы Accept() до тех пор, пока вы не будете к ним готовы.Любые попытки входящего подключения помещаются в очередь, созданную при открытии порта в режиме прослушивания.Как только невыполненная очередь в стеке заполнится, дальнейшие попытки подключения просто отбрасываются на пол.Возобновив работу с помощью Accepts(), вы быстро удалите очередь из очереди и будете готовы к новым соединениям.

2) Если вы действительно хотите, чтобы порт временно выглядел полностью закрытым, вы можете динамически применить к порту фильтр пакетов уровня ядра, чтобы предотвратить попытки входящих подключений достичь сетевого стека.Например, вы можете использовать Berkeley Packet Filter (BPF) на большинстве платформ *nix.То есть вы хотите отбрасывать входящие пакеты, поступающие в интересующий порт, используя функции брандмауэра платформы.Это, конечно, зависит от платформы, но это возможный подход.

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

Аналогично, любые соединения, ожидавшие закрытия прослушивающего сокета, будут закрыты без данных.

Если вы хотите подать сигнал восходящему балансировщику нагрузки, у вас должен быть протокол для этого.Не пытайтесь для этого злоупотреблять TCP.

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

Не существует явного метода отключения прослушивания!

Вы также можете close(fd) или shutdown(fd, how)

fd is the socket file descriptor you want to shutdown, and how is one of the following:

0 Further receives are disallowed

1 Further sends are disallowed

2 Further sends and receives are disallowed (like close())

На базовом уровне сокеты либо открыты, либо закрыты (здесь мы игнорируем тонкости диаграммы состояний TCP/IP).

Если ваш сокет закрыт, то ничто не сможет отправить в него данные.Если он открыт, то входящие данные будут приниматься и подтверждаться стеком TCP/IP до тех пор, пока алгоритм буферизации не сообщит «достаточно!».В этот момент дальнейшие данные не будут подтверждены.

У вас есть два варианта, которые я вижу.Либо закройте() сокет, когда вы хотите «отключить прослушивание», и снова откройте его позже. Используйте setockopt() с флагом SO_REUSEADDR, чтобы позволить вам повторно привязаться к известному порту до истечения срока действия TIME_WAIT2.

Другой вариант — оставить сокет открытым, но просто не использовать метод Accept(), пока вы «заняты».Предполагая, что у вас есть подтверждение запросов на уровне приложения, балансировщик нагрузки поймет, что не получает ответа, и будет действовать соответствующим образом.

Вот довольно уродливый подход, основанный на вашем отредактированном вопросе:

Откройте сокет для прослушивания с обычным бэклогом.Продолжить.

Если вы хотите «выключиться», откройте второй с отставанием 1 и SO_REUSEADDR.Закройте первый.Когда будете готовы возобновить работу, выполните еще одно переключение сокетов на сокет с обычным отставанием.

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

Я не думаю, что это хорошая идея, но...

Возможно, вам удастся позвонить Listen во второй раз.Спецификация POSIX не запрещает этого делать.Возможно, вы могли бы вызвать его второй раз с параметром невыполненной работы, равным 0, когда хотите «не прослушивать».

То, что происходит, когда прослушивание вызывается с отставанием 0, похоже, определяется реализацией.Об этом говорится в спецификации POSIX. может разрешить прием соединений, что означает, что некоторые реализации могут отклонять все соединения, если параметр backlog равен 0.Однако более вероятно, что ваша реализация выберет какое-то положительное значение, когда вы передадите 0 (вероятно, 1 или SOMAXCONN).

В вопросе не сказано, что за розетка.Если это сокет unix, вы можете остановить и начать прослушивание с помощью rename(2).Вы также можете навсегда прекратить прослушивание с помощью unlink(2), и, поскольку сокет остается открытым, вы можете продолжать обслуживать свой бэклог.Этот подход кажется весьма удобным, хотя я раньше не видел его использования и только изучаю его сам.

Вы уже получили ответы о невозможности сделать это через API сокетов.

Вы можете использовать другие методы ОС (т.е.Host firewewall/iptables/ipfilter), чтобы настроить временное правило отклонения.

Я обнаружил, что большинство балансировщиков нагрузки немного ограничены в возможностях распознавания проблем с подключением (большинство из них распознают RST только при проверке подключения, а не как ответ на легальную попытку подключения).

В любом случае, если вы ограничены зондами, обнаруживающими недоступность, вы настраиваете зонд уровня приложения, который выполняет HTTP-запрос или вход в систему FTP или аналогичные вещи, которые он распознает, если вы просто закроете после принятия.Он может даже интерпретировать сообщения об ошибках, такие как «500 служба недоступна», что мне в любом случае кажется более понятным.При использовании SNMP некоторые балансировщики нагрузки также могут использовать результат в качестве подсказки по нагрузке.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top