TCP: могут ли два разных сокета совместно использовать порт?
Это может быть очень простой вопрос, но он меня смущает.
Могут ли два разных подключенных разъема совместно использовать порт? Я пишу сервер приложений, который должен обрабатывать более 100 000 одновременных подключений, и мы знаем, что количество доступных портов в системе составляет около 60 000 (16 бит). Подключенный сокет назначается новому (выделенному) порту, поэтому это означает, что количество одновременных подключений ограничено количеством портов, если только несколько сокетов не могут использовать один и тот же порт. Итак, вопрос.
Спасибо за помощь в продвижении!
8 ответов
Серверный сокет прослушивает один порт. Все установленные клиентские соединения на этом сервере связаны с тем же портом прослушивания на стороне сервера соединения. Установленное соединение однозначно идентифицируется комбинацией пар IP/ порт на стороне клиента и на стороне сервера. Несколько соединений на одном сервере могут совместно использовать одну и ту же пару IP/ порт на стороне сервера, если они связаны с разными парами IP/ порт на стороне клиента, и сервер сможет обрабатывать столько клиентов, сколько позволяют доступные ресурсы системы. к.
На стороне клиента обычная практика для новых исходящих соединений - использовать случайный порт на стороне клиента, и в этом случае возможно исчерпать доступные порты, если вы делаете много соединений за короткий промежуток времени.
Прослушивание TCP / HTTP на портах: как много пользователей могут использовать один и тот же порт
Итак, что происходит, когда сервер прослушивает входящие соединения через порт TCP? Например, допустим, у вас есть веб-сервер на порту 80. Предположим, что ваш компьютер имеет публичный IP-адрес 24.14.181.229, а человек, пытающийся подключиться к вам, имеет IP-адрес 10.1.2.3. Этот человек может подключиться к вам, открыв сокет TCP на 24.14.181.229:80. Достаточно просто.
Интуитивно (и неправильно) большинство людей предполагает, что это выглядит примерно так:
Local Computer | Remote Computer
--------------------------------
<local_ip>:80 | <foreign_ip>:80
^^ not actually what happens, but this is the conceptual model a lot of people have in mind.
Это интуитивно понятно, потому что с точки зрения клиента он имеет IP-адрес и подключается к серверу по IP:PORT. Так как клиент подключается к порту 80, то его порт тоже должен быть 80? Это разумная вещь, чтобы думать, но на самом деле не то, что происходит. Если бы это было правильно, мы могли бы обслуживать только одного пользователя на внешний IP-адрес. Как только удаленный компьютер подключится, он подключит порт 80 к порту 80, и никто другой не сможет подключиться.
Три вещи должны быть поняты:
1.) На сервере процесс прослушивает порт. Получив соединение, он передает его другому потоку. Связь никогда не забирает порт прослушивания.
2.) Соединения однозначно идентифицируются ОС следующим 5-кратным набором: (локальный IP, локальный порт, удаленный IP, удаленный порт, протокол). Если какой-либо элемент в кортеже отличается, то это полностью независимое соединение.
3.) Когда клиент подключается к серверу, он выбирает случайный, неиспользуемый порт источника высокого порядка. Таким образом, один клиент может иметь до ~64 тыс. Подключений к серверу для одного и того же порта назначения.
Итак, это действительно то, что создается, когда клиент подключается к серверу:
Local Computer | Remote Computer | Role
-----------------------------------------------------------
0.0.0.0:80 | <none> | LISTENING
127.0.0.1:80 | 10.1.2.3:<random_port> | ESTABLISHED
Глядя на то, что на самом деле происходит
Сначала давайте используем netstat, чтобы увидеть, что происходит на этом компьютере. Мы будем использовать порт 500 вместо 80 (потому что на порте 80 происходит целая куча вещей, так как это общий порт, но функционально это не имеет значения).
netstat -atnp | grep -i ":500 "
Как и ожидалось, на выходе ничего нет. Теперь давайте запустим веб-сервер:
sudo python3 -m http.server 500
Теперь вот результат запуска netstat снова:
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
Итак, теперь есть один процесс, который активно прослушивает (State: LISTEN) на порте 500. Локальный адрес - 0.0.0.0, который является кодом для "прослушивания всех ip-адресов". Легкая ошибка - прослушивать только порт 127.0.0.1, который будет принимать соединения только с текущего компьютера. Так что это не соединение, это просто означает, что процесс запросил привязку () к IP-порту, и этот процесс отвечает за обработку всех подключений к этому порту. Это намекает на ограничение, что на каждый компьютер, прослушивающий порт, может быть только один процесс (есть способы обойти это с помощью мультиплексирования, но это гораздо более сложная тема). Если веб-сервер прослушивает порт 80, он не может использовать этот порт совместно с другими веб-серверами.
Итак, теперь давайте подключим пользователя к нашей машине:
quicknet -m tcp -t localhost:500 -p Test payload.
Это простой сценарий ( https://github.com/grokit/quickweb), который открывает сокет TCP, отправляет полезную нагрузку (в данном случае "Проверка полезной нагрузки"), ждет несколько секунд и отключается. Повторное выполнение команды netstat покажет следующее:
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
tcp 0 0 192.168.1.10:500 192.168.1.13:54240 ESTABLISHED -
Если вы подключитесь к другому клиенту и снова выполните netstat, вы увидите следующее:
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
tcp 0 0 192.168.1.10:500 192.168.1.13:26813 ESTABLISHED -
... то есть клиент использовал другой случайный порт для соединения. Таким образом, никогда не бывает путаницы между IP-адресами.
Подключенный сокет назначается новому (выделенному) порту
Это обычная интуиция, но она неверна. Подключенный сокет не назначен новому / выделенному порту. Единственным фактическим ограничением, которому должен удовлетворять стек TCP, является то, что кортеж (local_address, local_port, remote_address, remote_port) должен быть уникальным для каждого соединения сокета. Таким образом, на сервере может быть много сокетов TCP, использующих один и тот же локальный порт, если каждый из сокетов порта подключен к другому удаленному местоположению.
См. Параграф "Пара гнезд" по адресу: http://books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq=socket%20pair%20tuple&pg=PA52
Теоретически да. Практика, нет. Большинство ядер (включая linux) не дают вам ни секунды bind()
на уже выделенный порт. Это был не очень большой патч, чтобы сделать это возможным.
Концептуально, мы должны различать сокет и порт. Сокеты - это конечные точки двунаправленной связи, то есть "вещи", в которые мы можем отправлять и получать байты. Это концептуальная вещь, в заголовке пакета с именем "socket" такого поля нет.
Порт - это идентификатор, который способен идентифицировать сокет. В случае TCP порт является 16-разрядным целым числом, но существуют и другие протоколы (например, в сокетах Unix "порт" по сути является строкой).
Основная проблема заключается в следующем: если приходит входящий пакет, ядро может идентифицировать свой сокет по номеру порта назначения. Это наиболее распространенный способ, но это не единственная возможность:
- Сокеты могут быть идентифицированы по IP-адресу назначения входящих пакетов. Это имеет место, например, если у нас есть сервер, использующий два IP одновременно. Затем мы можем запустить, например, разные веб-серверы на тех же портах, но на разных IP-адресах.
- Сокеты могут быть идентифицированы по их исходному порту и IP-адресу. Это имеет место во многих конфигурациях балансировки нагрузки.
Поскольку вы работаете на сервере приложений, он сможет это сделать.
Думаю, ни один из ответов не описывает каждую деталь процесса, так что вот оно:
Рассмотрим HTTP-сервер:
Он просит ОС привязать порт 80 к одному или нескольким IP-адресам (если вы выберете 127.0.0.1, принимаются только локальные подключения. Вы можете выбрать 0.0.0.0 для привязки ко всем IP-адресам (локальный хост, локальная сеть, глобальная сеть) , обе версии IP)).
Когда клиент подключается к этому порту, он БУДЕТ блокировать его на некоторое время (вот почему у сокета есть отставание: он ставит в очередь несколько попыток подключения, потому что они НЕ мгновенные).
Затем ОС выбирает случайный порт и передает это соединение на этот порт (считайте его временным портом, который с этого момента будет обрабатывать весь трафик).
Затем порт 80 освобождается для следующего подключения (сначала он примет первое в очереди).
Когда клиент или сервер отключаются, случайный порт некоторое время остается открытым (CLOSE_WAIT на удаленной стороне, TIME_WAIT на локальной стороне). Это позволяет сбрасывать некоторые потерянные пакеты по пути. Время по умолчанию для этого состояния составляет 2 * MSL секунды (и оно БУДЕТ потреблять память во время ожидания).
После этого ожидания этот случайный порт снова становится свободным для приема других подключений.
Таким образом, TCP не может даже разделять порт между двумя IP-адресами!
Нет. Невозможно использовать один и тот же порт в определенный момент. Но вы можете сделать свое приложение таким, чтобы оно осуществляло доступ к порту в разное время.
Абсолютно нет, потому что даже несколько подключений могут использовать одни и те же порты, но у них будут разные IP-адреса.
Нет, два разных подключенных сокета не могут использовать один и тот же номер порта. В стеке протоколов TCP/IP сокет идентифицируется уникальной комбинацией IP- адреса и номера порта. Таким образом, два сокета могут быть привязаны к одному и тому же номеру порта на одном хосте, если они используют разные IP-адреса.