Ниже вы видите программу на python, которая действует как сервер, прослушивающий запросы на подключение к порту 9999:
Ответ 1
ПРИМЕЧАНИЕ. Ответы оформляются без какого-либо опыта в Python, но вопросы не имеют отношения к языку, на который нужно отвечать.
Что это за очереди?
Проще говоря, параметр backlog указывает количество ожидающих соединений, которые будет удерживать очередь.
Когда к серверу подключается несколько клиентов, сервер удерживает входящие запросы в очереди. Клиенты располагаются в очереди, и сервер обрабатывает их запросы один за другим, как и когда участник очереди продолжает работу. Природа такого типа соединения называется соединением в очереди.
Имеет ли это значение для запросов клиентов? (Я имею в виду, отличается ли сервер, работающий с socket.listen(5)
от сервера, работающего с socket.listen(1)
при принятии запросов на подключение или при получении данных?)
Да, оба случая разные. В первом случае можно было бы разместить только 5 клиентов в очереди; тогда как в случае backlog = 1 в очереди может удерживаться только 1 соединение, что приводит к удалению дальнейшего запроса на соединение!
Почему минимальное значение равно нулю? Не должно ли быть хотя бы 1?
Я понятия не имею о Python, но, согласно этому источнику, в C аргумент backlog 0 может позволить сокету принимать соединения, и в этом случае длина очереди прослушивания может быть установлена на минимальное значение, определяемое реализацией.
Есть ли предпочтительное значение?
На этот вопрос нет четкого ответа. Я бы сказал, что это зависит от характера вашего приложения, а также от конфигурации оборудования и программного обеспечения. Опять же, согласно источнику, BackLog
тихо ограничен от 1 до 5 включительно (опять же, согласно C).
Это отставание определено только для TCP-соединений или оно также применимо для UDP и других протоколов?
NO. Обратите внимание, что нет необходимости слушать() или принять() для неподключенных дейтаграммных сокетов (UDP). Это один из преимуществ использования неподключенных дейтаграмм сокетов!
Но имейте в виду, что существуют реализации сокетов дейтаграмм на основе TCP (называемые TCPDatagramSocket), которые имеют параметр backlog.
Ответ 2
Когда устанавливается соединение TCP, выполняется так называемое трехстороннее рукопожатие. Обе стороны обмениваются некоторыми пакетами, и, как только они это сделают, это соединение называется завершенным, и оно готово для использования приложением.
Однако это трехстороннее рукопожатие занимает некоторое время. И в течение этого времени соединение ставится в очередь, и это отставание. Таким образом, вы можете установить максимальное количество незавершенных параллельных соединений с помощью .listen(no)
(обратите внимание, что в соответствии со стандартом posix значение является лишь подсказкой, оно может быть полностью проигнорировано). Если кто-то попытается установить соединение выше лимита невыполненных обязательств, другая сторона откажется от него.
Таким образом, предел невыполненных заданий относится к незавершенным соединениям, но не установлен.
Теперь более высокий лимит отставания будет лучше в большинстве случаев. Обратите внимание, что максимальный лимит зависит от ОС, например, cat/proc/sys/net/core/somaxconn
дает мне 128
на моем Ubuntu.
Ответ 3
Похоже, что функция этого параметра заключается в ограничении количества входящих запросов на подключение, которые сервер будет хранить в очереди, предполагая, что он может обслуживать текущий запрос и небольшое количество ожидающих запросов в очереди в течение разумного периода времени при высокой нагрузке. Вот хороший параграф, с которым я столкнулся, который дает небольшой контекст вокруг этого аргумента...
Наконец, аргумент для прослушивания сообщает библиотеке сокетов, что мы хотим, чтобы она поставила в очередь до 5 запросов на подключение (нормальный максимум) перед отказом от внешних подключений. Если остальная часть кода написана правильно, этого должно быть достаточно.
https://docs.python.org/3/howto/sockets.html#creating-a-socket
Ранее в документе был текст, предлагающий клиентам подключаться и отключаться от сервера, чтобы вы не создавали длинную очередь запросов в первую очередь...
Когда connect
завершается, сокет s
можно использовать для отправки запроса на текст страницы. Тот же сокет будет читать ответ, а затем будет уничтожен. Это верно, уничтожено. Клиентские сокеты обычно используются только для одного обмена (или небольшого набора последовательных обменов).
Связанное руководство HowTo является обязательным для прочтения при ознакомлении с сетевым программированием с использованием сокетов. Это действительно фокусирует внимание на некоторых крупных картинных темах. Теперь, как серверный сокет управляет этой очередью, насколько подробности реализации - это другая история, возможно, интересная. Я полагаю, что мотивация для этого дизайна более показательна, без этого барьер для атаки типа "отказ в обслуживании" был бы очень и очень низким.
Что касается причины минимального значения 0 против 1, мы должны помнить, что 0 по-прежнему является допустимым значением, то есть ничего не ставить в очередь. По сути, это означает, что очереди запросов не будет, просто отклоните соединения, если в данный момент серверный сокет обслуживает соединение. В этом контексте всегда следует помнить о точке обслуживания активного соединения, единственной причиной, по которой очередь будет интересна в первую очередь.
Это подводит нас к следующему вопросу о предпочтительной стоимости. Это все дизайнерское решение, хотите ли вы ставить запросы в очередь или нет? Если это так, вы можете выбрать значение, которое, по вашему мнению, оправдано, исходя из ожидаемого трафика и известных аппаратных ресурсов, я полагаю. Я сомневаюсь, что есть что-то формальное в выборе значения. Это заставляет меня задуматься о том, насколько легким является запрос в первую очередь, когда вы будете подвергнуты штрафу за размещение чего-либо на сервере.
ОБНОВИТЬ
Я хотел обосновать комментарии от user207421 и пошел искать источник на python. К сожалению, этот уровень детализации находится не в источнике sockets.py, а в socketmodule.С# L3351-L3382 с хешем 530f506.
Комментарии очень полезны, я скопирую дословный источник ниже и выделю поясняющие комментарии, которые довольно освещают...
Мы стараемся выбрать достаточно высокий уровень невыполненных заданий по умолчанию, чтобы избежать обрывов соединений для обычных рабочих нагрузок, но не слишком высокий, чтобы ограничить использование ресурсов.
а также
Если задано отставание, оно должно быть не менее 0 (если оно меньше, оно равно 0); в нем указывается количество неприемлемых соединений, которые система разрешит перед отказом в новых соединениях. Если не указан, выбирается разумное значение по умолчанию.
/* s.listen(n) method */
static PyObject *
sock_listen(PySocketSockObject *s, PyObject *args)
{
/* We try to choose a default backlog high enough to avoid connection drops
* for common workloads, yet not too high to limit resource usage. */
int backlog = Py_MIN(SOMAXCONN, 128);
int res;
if (!PyArg_ParseTuple(args, "|i:listen", &backlog))
return NULL;
Py_BEGIN_ALLOW_THREADS
/* To avoid problems on systems that don't allow a negative backlog
* (which doesn't make sense anyway) we force a minimum value of 0. */
if (backlog < 0)
backlog = 0;
res = listen(s->sock_fd, backlog);
Py_END_ALLOW_THREADS
if (res < 0)
return s->errorhandler();
Py_RETURN_NONE;
}
PyDoc_STRVAR(listen_doc,
"listen([backlog])\n\
\n\
Enable a server to accept connections. If backlog is specified, it must be\n\
at least 0 (if it is lower, it is set to 0); it specifies the number of\n\
unaccepted connections that the system will allow before refusing new\n\
connections. If not specified, a default reasonable value is chosen.");
Пройдя дальше по кроличьей норе во внешнее пространство, я обнаружил следующий источник из модуля сокетов...
res = listen(s->sock_fd, backlog);
Этот источник заканчивается в socket.h и укорочен, используя Linux в качестве конкретной платформы фона для целей обсуждения.
/* Maximum queue length specifiable by listen. */
#define SOMAXCONN 128
extern int __sys_listen(int fd, int backlog);
Там больше информации можно найти в справочной странице
http://man7.org/linux/man-pages/man2/listen.2.html
int listen(int sockfd, int backlog);
И соответствующая документация
listen()
помечает сокет, называемый sockfd
как пассивный сокет, то есть как сокет, который будет использоваться для приема входящих запросов на соединение с помощью accept
(2).
Аргумент sockfd
является дескриптором файла, который ссылается на сокет типа SOCK_STREAM
или SOCK_SEQPACKET
.
Аргумент backlog
определяет максимальную длину, до которой может увеличиваться очередь ожидающих соединений для sockfd
. Если запрос соединения приходит, когда очередь заполнена, клиент может получить ошибку с указанием ECONNREFUSED
или, если базовый протокол поддерживает повторную передачу, запрос может быть проигнорирован, так что последующая ECONNREFUSED
соединения будет ECONNREFUSED
.
Один дополнительный источник определяет ядро как ответственное за очередь невыполненных работ.
Второй аргумент backlog этой функции указывает максимальное количество соединений, которое ядро должно поставить в очередь для этого сокета.
Далее они кратко расскажут о том, как недопустимые/поставленные в очередь соединения распределяются в заделе (полезный рисунок включен в связанный источник).
Чтобы понять аргумент backlog, мы должны понимать, что для данного сокета прослушивания ядро поддерживает две очереди:
Неполная очередь соединения, которая содержит запись для каждого SYN, полученного от клиента, для которого сервер ожидает завершения трехстороннего рукопожатия TCP. Эти сокеты находятся в состоянии SYN_RCVD
(рисунок 2.4).
Завершенная очередь соединений, которая содержит запись для каждого клиента, с которым завершено трехстороннее рукопожатие TCP. Эти сокеты находятся в состоянии ESTABLISHED
(рисунок 2.4). Эти две очереди изображены на рисунке ниже:
Когда в неполной очереди создается запись, параметры из сокета прослушивания копируются во вновь созданное соединение. Механизм создания соединения полностью автоматический; серверный процесс не задействован.