Как работает функция accept () API сокетов?

126

API сокетов де-факто является стандартом для связи TCP / IP и UDP / IP (то есть сетевого кода в том виде, в каком мы его знаем). Однако одна из его основных функций accept()немного волшебна.

Чтобы заимствовать полуформальное определение:

accept () используется на стороне сервера. Он принимает полученную входящую попытку создать новое TCP-соединение от удаленного клиента и создает новый сокет, связанный с парой адресов сокета этого соединения.

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

Как acceptработает? Как это реализовано? По этой теме много путаницы. Многие утверждают, что accept открывает новый порт, через который вы общаетесь с клиентом. Но это, очевидно, неправда, поскольку новый порт не открывается. На самом деле вы можете общаться через один порт с разными клиентами, но как? Когда несколько потоков обращаются recvк одному и тому же порту, как данные узнают, куда идти?

Я предполагаю, что это что-то вроде адреса клиента, связанного с дескриптором сокета, и всякий раз, когда данные проходят через recvнего, они направляются в правильный сокет, но я не уверен.

Было бы здорово получить подробное объяснение того, как работает этот механизм.

Эли Бендерский
источник
2
поэтому для каждого запроса клиента открывается совершенно НОВОЕ соединение сокета на стороне сервера. Сервер должен быть открыт на 80, чтобы всегда слушать входящие звонки. Если он получает вызов, он немедленно создает НОВЫЙ сокет с четырьмя кортежами, как указано ниже, который устанавливает TCP-соединение между клиентом и сервером. Я правильно понимаю?
мозговой штурм
1
Это очень принципиальный вопрос , и я недавно был испытан на это в интервью: stackoverflow.com/questions/24871827/... Если у вас есть какие - либо комментарии по этому поводу , пожалуйста , напишите
мозговой штурм
@brainstorm Только если вы полностью игнорируете существование HTTP keep-alive.
Маркиз Лорн,

Ответы:

140

Ваше замешательство заключается в том, что вы думаете, что сокет идентифицируется как IP-адрес сервера: порт сервера. На самом деле сокеты однозначно идентифицируются по квартету информации:

Client IP : Client Port и Server IP : Server Port

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

Пример для пояснения:

Скажем, у нас есть сервер в 192.168.1.1:80и два клиента, 10.0.0.1и 10.0.0.2.

10.0.0.1открывает соединение на локальном порту 1234и подключается к серверу. Теперь у сервера есть один сокет, идентифицированный следующим образом:

10.0.0.1:1234 - 192.168.1.1:80  

Теперь 10.0.0.2открывает соединение на локальном порту 5678и подключается к серверу. Теперь у сервера есть два сокета, обозначенных следующим образом:

10.0.0.1:1234 - 192.168.1.1:80  
10.0.0.2:5678 - 192.168.1.1:80
17 из 26
источник
3
Я не знаю деталей реализации (которые, вероятно, различаются от платформы к платформе), я просто знаю, что концептуально сокеты идентифицируются квартетом информации, которую я описал.
17 из 26,
3
У вас есть ссылки на это?
qeek 07
3
Случайный вопрос: что произойдет, если используется NAT, и два клиента в одной сети пытаются использовать один и тот же локальный порт при подключении к серверу? Например, если 10.0.0.1 и 10.0.0.2 оба подключены к маршрутизатору с внешним IP-адресом 192.168.0.1, то сервер на 192.168.1.1 видит два соединения с 192.168.0.1. Что произойдет в том случае, если по воле генератора случайных чисел 10.0.0.1 и 10.0.0.2 выберут один и тот же локальный порт?
aroth
4
Поддержка NAT в маршрутизаторе позаботится о деталях. Сетевой трафик фактически проходит через два соединения - клиент-маршрутизатор и маршрутизатор-сервер. Маршрутизатор устанавливает исходящие соединения на двух разных портах 192.168.0.1:1234 и 192.168.0.1:5678. Затем входящий трафик перенаправляется маршрутизатором нужному клиенту.
17 из 26,
3
Если сокет идентифицируется квартетом, что представляет собой квартетная информация прослушивающего сокета?
Эрик Чжэн
74

Просто чтобы добавить к ответу пользователя "17 из 26"

На самом деле сокет состоит из 5 кортежей - (исходный IP-адрес, исходный порт, целевой IP-адрес, целевой порт, протокол). Здесь протоколом может быть TCP или UDP или любой протокол транспортного уровня. Этот протокол идентифицируется в пакете из поля «протокол» дейтаграммы IP.

Таким образом, возможно, что разные приложения на сервере будут взаимодействовать с одним и тем же клиентом с помощью одних и тех же четырех кортежей, но с разными полями протокола. Например

Apache на стороне сервера разговаривает (server1.com:880-client1:1234 по TCP) и World of Warcraft разговаривает (server1.com:880-client1:1234 по UDP)

И клиент, и сервер будут обрабатывать это, поскольку поле протокола в IP-пакете в обоих случаях отличается, даже если все остальные 4 поля одинаковы.

Митос
источник
13

Когда я изучал это, меня смущало то, что термины socketи portпредполагают, что они являются чем-то физическим, хотя на самом деле это просто структуры данных, которые ядро ​​использует для абстрагирования деталей сети.

Таким образом, структуры данных реализованы так, чтобы иметь возможность отделить соединения от разных клиентов. Что касается того, как они реализованы, ответ будет либо а.) Не имеет значения, цель API сокетов именно в том, чтобы реализация не имела значения, или б.) Просто посмотрите. Помимо настоятельно рекомендуемых книг Стивенса, содержащих подробное описание одной реализации, ознакомьтесь с исходным кодом в Linux, Solaris или одной из BSD.

a2800276
источник
Да, большая часть сетевой терминологии просто присваивает имена определенным наборам битов и решениям, принимаемым на основе их значений («идентификатор протокола», «маршрутизация», «привязка», «сокет» и т. Д.). Все сетевые карты аппаратного средства предназначены для приема является потоком бит. Что с ними происходит по отношению к программам на вашем компьютере, решают драйвер и ОС. Мы могли бы избавиться от всей этой терминологии завтра, если бы захотели, но принцип доставки потока битов кажется фундаментальным ...
masterxilo
-1

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

Серверный процесс, запущенный на IP-адресе сервера, поддерживает базу данных (то есть меня не волнует, какую таблицу / список / дерево / массив / магическую структуру данных он использует) активных сокетов и прослушивает порт сервера. Когда он получает сообщение (через стек TCP / IP сервера), он проверяет клиентский IP и порт по базе данных. Если IP-адрес клиента и порт клиента находятся в записи базы данных, сообщение передается существующему обработчику, в противном случае создается новая запись в базе данных и создается новый обработчик для обработки этого сокета.

На заре ARPAnet определенные протоколы (например, FTP) слушали указанный порт для запросов на соединение и отвечали портом передачи обслуживания. Дальнейшая связь для этого соединения будет проходить через порт передачи обслуживания. Это было сделано для повышения производительности отдельных пакетов: в те дни компьютеры были на несколько порядков медленнее.


источник
не могли бы вы подробнее рассказать о «порте передачи обслуживания»?
Эли Бендерски
1
Это либо описание какого-то протокола до TCP, либо чрезмерно упрощенное. Клиент, пытающийся подключиться к прослушивающему сокету, отправляет специальный пакет для установления соединения (установлен бит SYN). Существует четкое различие между пакетом, создающим новый сокет, и пакетом, использующим существующий сокет.
John M
... отправляет специальный пакет для установления соединения (установлен бит SYN). Что (как я понимаю) заставляет стек протокола передавать его «слушателю» (если есть), поэтому может быть только один порт прослушивания на комбинацию адреса / порта / протокола. Я не уверен, есть ли это в спецификации или просто в соглашении о реализации.
Peter Wone
1
Второй абзац неправильно описывает, что происходит на уровне TCP или внутри серверного процесса. Серверным процессам не нужно поддерживать структуры данных сокетов любого типа или проверять входящие пары IP: Port на что-либо вообще. Вот для чего нужны сокеты. FTP использует отдельный порт для данных, а не для всех «дальнейших коммуникаций», и это сделано для упрощения протокола, а не из соображений производительности. Использование нового порта при этом никак не улучшает производительность.
Marquis of Lorne
"поддерживает базу данных (это означает, что мне все равно, какую таблицу / список / дерево / массив / магическую структуру данных она использует)" :) Я обычно называю это "Таблица" (или, может быть, "График" или "Дерево решений" ). «База данных» предлагает мне некоторую реализацию.
masterxilo