Сервер против клиентского сокета (детали низкого уровня)? - PullRequest
13 голосов
/ 22 апреля 2009

Общее программирование: в методе сокета сервера accept (), что именно происходит. На низком уровне, как серверные сокеты отличаются от клиентских сокетов?

Ответы [ 4 ]

29 голосов
/ 25 апреля 2009

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

Серверные сокеты будут вызывать bind() для связи с портом. Они хотят быть связанными с портом, чтобы другие программы знали, где их найти. Клиентские сокеты могут звонить bind(), но почти никогда не делают, потому что нет особого смысла. Если сокет не вызывает bind(), ОС просто выберет для него временный порт, что хорошо для клиентов, потому что они выполняют вызов; никому не нужно , чтобы звонить им.

Серверные сокеты вызывают listen(). Это было хорошо объяснено в других ответах.

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

Ключ к пониманию того, как прослушивающий сокет продолжает слушать, пока принятое соединение делает свое дело, заключается в понимании того, что соединения tcp зависят от 4-х наборов (1) локального адреса (2) локального порта (3) внешнего адреса (4) иностранный порт. Они определяют уникальную связь. Перед тем, как accept() передал обратно новый сокет, ядро ​​использовало эти значения для создания различных структур, так что в сотрудничестве со стеком tcp / ip весь трафик с этим кортежем будет направляться в подключенный сокет. Даже если ваш сервер может иметь тысячу соединений с локальным адресом 192.168.1.100, порт 80, клиентская комбинация адреса и порта всегда будет отличаться, и, следовательно, кортеж всегда уникален.

14 голосов
/ 22 апреля 2009

Во-первых, серверные сокеты обычно связаны с хорошо известными именами (в данном случае с портами), и они устанавливаются с listen(). Вот где происходит реальная разница, поскольку клиентские сокеты устанавливаются с connect(). Вызов listen() для сокета приводит к тому, что реализация ядра tcp / ip начинает принимать соединения, отправленные на связанное имя сокета (порт). Это произойдет независимо от того, звоните вы или нет accept().

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

10 голосов
/ 22 апреля 2009

Если вы действительно заинтересованы, то я бы посоветовал вам прочитать TCP / IP Illustrated, Volume 2 . Если вы хотите получить ответ «меньше в духе», то:

  • Серверные сокеты связаны с известными конечными точками, где конечной точкой является кортеж (протокол, адрес, порт). Конечная точка формируется из протокола, указанного в системном вызове socket(), и информации об адресации, указанной в системном вызове bind().
  • Когда сервер вызывает системный вызов listen(), сетевой стек создает очередь, в которую помещаются ожидающие соединения. Подсказка к размеру очереди дается в качестве параметра backlog для listen().
  • Затем сервер вызывает accept(), чтобы извлечь новое соединение из очереди.
  • Клиентские сокеты отправляют сообщения на серверные сокеты, указывая конечную точку сервера при вызове на connect(), а затем отправляя данные с помощью send() или write().
  • Когда клиент вызывает connect(), соединение помещается в очередь на стороне сервера, где оно находится, пока сервер не примет соединение .

Однако это описание действительно только для сокетов TCP / IP. Случай UDP проще и совершенно другой, так как сокеты UDP не обязательно подключены.

3 голосов
/ 22 апреля 2009

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

Если s является socket объектом, то сервер сначала привязывается к порту, вызывая s.bind(('',12345)). Это создает сокет в режиме сервера. Готов к перехвату данных через порт 12345.

Затем человек звонит s.listen(10). Это переводит сокет в режим сервера. Это означает, что запрос на соединение будет получен этим сокетом (до 10 ожидающих запросов одновременно).

К тому времени, когда мы доберемся до s.accept(), операционная система уже знает, что мы прослушиваем порт 12345. s.accept() просто говорит, что мы собираемся делать с полученными нами запросами. В python s.accept() вернет (connection,address), где соединение - это соединение через другой порт. В этом случае connection не очень отличается от объекта сокета, который открыл клиент. Это достаточно симметрично с этого момента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...