Разбираемся с сокетами на примере стандартной библиотеки Go
Сокеты являются основой почти любого сетевого приложения, даже если их использование скрыто от ваших глаз. Наша цель — разобраться, что это такое, и написать простое приложение для взаимодействия по сети. Реализуем мы это приложение на языке Go, но принципы использования сокетов одинаковы для любых языков программирования, а библиотеки имеют очень похожий API.
Модель TCP/IP
Сетевое взаимодействие можно описать с помощью стека протоколов, которые его обеспечивают. Сетевой протокол — это набор правил, с помощью которых можно соединиться и обмениваться данными с другим устройством сети. Эта задача включает в себя множество подзадач: кодирование данных в физические сигналы, поиск получателя в сети, обеспечение надежной передачи, шифрование трафика и так далее. Поэтому и существует такое понятие как стек протоколов: протоколы разных уровней решают разные задачи. При этом протоколы одного уровня решают поставленную задачу по-разному и является взаимозаменяемыми без влияния на протоколы других уровней.
Существуют разные модели представления сетевого взаимодействия. Одной из самых популярных является модель TCP/IP, названная по двум самым популярным протоколам современной глобальной сети. Она включает в себя четыре уровня. На самом низком уровне — уровне сетевых интерфейсов, решается задача физического доступа и кодирования информации. Сетевой уровень соединяет сети всего мира в глобальную сеть. Главный протокол этого уровня — IP. Он определяет адресацию (IP-адреса), благодаря которой устройства могут связываться друг с другом на любых расстояниях. Какие задачи решает транспортный уровень мы обсудим в следующем разделе. На прикладном же уровне работают большинство сетевых приложений компьютера. Они определяют свои протоколы под конкретные задачи.
Транспортный уровень
Мы не будем сегодня разбираться в маршрутизации, коммутации и других задачах, решаемых на уровнях ниже транспортного. Можно считать, что на этом уровне мы уже знаем, какой маршрут нужно выбрать, чтобы физически доставить пакет до получателя. Однако, часто бывает недостаточно отправить пакет в сеть и надеяться, что он достигнет цели. Есть множество причин, по которым данные могут потеряться или исказиться в пути. Это могут быть помехи на физическом уровне, превышение максимального размера пакета (MTU) на канальном уровне, переполнение буфера коммутатора и много чего еще. Кроме того, пакеты могут прийти в другом порядке по сравнению с тем, как были отправлены.
Чтобы программисту не нужно было контролировать доставку пакетов, их целостность и порядок, эти задачи возложены на транспортный уровень стека сетевых протоколов. Есть два наиболее популярных протокола на этом уровне.
Протокол UDP (User Datagram Protocol) противоречит той рекламе, которую я только что дал транспортному уровню. Он является тонкой оберткой над протоколом IP и не обеспечивает никакого контроля над доставкой. Программист берет данные для отправки, упаковывает их в UDP-пакет, размер которого выбирает самостоятельно, и отправляет в сеть. Если пакет не достигнет цели, ни отправитель, ни получатель об этом не узнают. Этот протокол часто применяется в задачах, когда важна скорость доставки, но не столь важно, чтобы все пакеты были доставлены. Например, в онлайн-играх или видео-конференциях.
Протокол, который способен проконтролировать доставку вашего пакета, называется TCP (Transmission Control Protocol). В отличие от UDP, перед передачей данных он формирует виртуальное соединение. Для этого используются пакеты специального назначения, которыми обмениваются инициатор соединения (будем считать, что это клиент) и сервер. Такая процедура называется трехфазным рукопожатием (3-way handshake).
Другим отличием от UDP является то, что программист при работе с TCP не заботится о размере данных, которые будет отправлять. Этот протокол опрерирует понятием потока и самостоятельно нарезает этот поток на пакеты того размера, который посчитает нужным. Главная же особенность протокола TCP в контроле доставки данных. Он будет отправлять нужные пакеты снова, пока получатель не подтвердит (тоже средствами TCP), что они доставлены и прошли контроль целостности.
Сокеты
Транспортный уровень обеспечивает доставку пакета до нужного компьютера, но операционной системе нужно понять, какому приложению предназначается этот пакет. Для этой цели в ОС используются сетевые порты. Порт — это обычное 16-битное число, которое соответствует процессу и сетевому соединению, которое он использует. Извлекая порт получателя из заголовка TCP-пакета, операционная система определяет, какому процессу нужно передать данные.
Таким образом, чтобы доставить данные из процесса на одном устройстве в процесс на другом, необходимо знать IP-адрес получателя и порт, используемый этим процессом. Пара этих параметров образует понятие сокета. В то же время, сокетом называют ресурс операционной системы, который она выделяет, когда процесс запрашивает порт для сетевого взаимодействия. По факту, это два взгляда на одно и то же. Рассмотрим стандартную процедуру, когда клиентское приложение хочет связаться с сервером.
Допустим, клиент каким-то образом знает, какой порт нужно указать в качестве получателя в пакете. Чтобы принимать пакеты, сервер должен быть готов к этому. Это возможно, засчет открытых портов. Они постоянно “слушают” входящие соединения, которых может быть больше одного, в отличие от обычных (закрытых) портов. Как правило, службы, к которым хотят получить доступ процессы на других устройствах, имеют общеизвестные открытые порты, что решает проблему узнавания, к какому порту обращаться клиенту. Клиентские же процессы обычно могут брать произвольный незанятый порт для взаимодействия по сети.
Далее мы реализуем клиент-серверное взаимодействие с помощью сокетов на языке Go. Как уже было сказано, работа с сокетами представлена очень похожим API в разных языках программирования. Поэтому мы постараемся выделить основные моменты, не заостряясь на языковых особенностях.
Начнем с более популярного протокола TCP. Наша задача — реализовать простейший, не очень общительный сервер, который здоровается с клиентом, узнает имя собеседника и прощается с ним. Классическая реализация TCP-сервера:
- Создать слушающий сокет с открытым портом.
- В бесконечном цикле принимать соединения от клиентов.
- При соединении с клиентом создается новый сокет для взаимодействия только с этим клиентом.
- Для каждого соединения обрабатывать клиентские запросы в отдельном потоке.
Компьютерные сети являются обширной темой, о которой нужно иметь представление любому бэкенд-разработчику. Сегодня мы рассмотрели сокеты, с помощью которых программист может обеспечить взаимодействовие с другими устройствами сети на транспортном уровне.
Written on February 17th, 2019 by Alexey Kalina
Что такое сокет и зачем он нужен
Эта статья расширяет ваш кругозор в вопросах устройства компьютеров и их софта. Текст про важную вещь в сетевой архитектуре, и он будет полезен тем, кто собирается проектировать софт, как-то связанный с интернетом.
Чтобы разобраться в том, что такое сокеты и чем они так полезны, нужно кое-что вспомнить из статей про IP-адреса и про порты в программировании. Вот короткая выжимка из этих статей:
- У каждого компьютера в сети есть IP-адрес, даже если это просто локальная сеть.
- IP-адрес — это четыре числа от 0 до 255, разделённые точками, например 77.88.55.88 (это адрес сервера Яндекса).
- С помощью этих адресов компьютеры знают, куда направить свои запросы и ответы.
- Когда один компьютер соединяется с другим, они это делают через сетевой порт. Можно сказать, что порт — это номер соединения.
- Сетевые порты в компьютере нумеруются от 1 до 65535, а компьютер сам следит за тем, как распределяются эти номера.
- С помощью портов компьютер понимает, какие данные предназначены какой программе.
- Некоторые программы и соединения всегда используют один и тот же порт, а другие получают его случайным образом.
Главное: что такое сокет
Сокет — это виртуальная конструкция из IP-адреса и номера порта. Её придумали для того, чтобы разработчикам было проще писать код, а программы могли передавать данные друг другу даже в пределах одного компьютера.
⭐ Можно представить, что сокет — это виртуальная труба, которую строят между двумя приложениями, чтобы гонять между ними данные. Приложения видят только концы трубы, а как проходит трубопровод — они не знают и им неважно.
Смысл в том, чтобы программист работал не с IP-адресами и портами, разбираясь в тонкостях работы протоколов, а использовал что-то попроще. В итоге получается так:
- программист пишет в программе, что он хочет сделать новый сокет;
- указывает для него IP-адрес, если это необходимо;
- программа собирает это в виртуальную конструкцию, и получается сокет;
- после этого программист может отправлять данные просто в сокет и принимать их оттуда, а компьютер берёт на себя все вопросы по передаче данных.
Для чего нужны сокеты
Сокеты используют для двух вещей:
Как работает передача по сети, расскажем ниже, а сейчас поговорим про связь между приложениями. Идея в том, что если на компьютере запустить два приложения и в каждом из них настроить сокеты, то можно передавать данные из одного в другое даже без API. Например, на внутренних сокетах работают многие служебные программы — так они передают данные в операционную систему.
На сокетах работает половина интернета. Например, чтобы получить данные из мобильного приложения, сервер запускает у себя сокет для связи с приложением. Каждое приложение тоже открывает свой сокет, связывается через него с сервером, и так они обмениваются данными.
Но сокет на сервере один, а желающих подключиться к нему — много. Чтобы решить эту проблему, сервер копирует сокеты.
Копирование сокетов и множественные подключения
Когда на сервер поступает запрос на соединение с сокетом, он не устанавливает связь напрямую, а копирует этот сокет и настраивает связь через него. После копирования сервер запоминает, какая копия отвечает за какое соединение, и дальше просто обрабатывает все запросы по очереди. При этом исходный сокет остаётся нетронутым — он не используется для связи напрямую, а служит шаблоном для создания копий.
Если бы сервер так не делал, то с ним могло бы соединиться только одно приложение — первое, которое успело подключиться.
Что дальше
А дальше сделаем проект — напишем серверную и клиентскую часть и будем передавать данные между ними через сокеты, чтобы увидеть, как это работает в жизни. Подпишитесь, чтобы не пропустить проект.
Этот рекламный блок сообщает, что после обучения в «Яндекс Практикуме» вы можете получить высокооплачиваемую работу разработчика, тестировщика, аналитика, менеджера и дата-сайентиста.
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.