Как работает API-интерфейс socket ()? - PullRequest
111 голосов
/ 28 января 2009

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

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

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

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

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

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

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

Ответы [ 4 ]

124 голосов
/ 28 января 2009

Ваша путаница заключается в том, что вы думаете, что сокет идентифицируется по 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
64 голосов
/ 05 марта 2009

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

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

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

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

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

11 голосов
/ 30 января 2009

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

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

1 голос
/ 28 января 2009

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

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

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

...