Что такое сокет в сети
Перейти к содержимому

Что такое сокет в сети

  • автор:

Программирование сокетов. Сеть и сокет

Web Sockets (веб-сокеты) — это технология, позволяющая создавать интерактивное соединение для обмена сообщения в онлайн-режиме. Соединение создаётся в сети между сервером и клиентом (браузером). В отличие от протокола HTTP, веб-сокеты позволяют работать с 2-направленным потоком данных, поэтому можно с уверенностью сказать, что технология является уникальной. Для наилучшего понимания давайте разберём особенности работы сокетов и посмотрим, а чем они конкретно отличаются от HTTP.

Как функционирует HTTP?

Про протокол HTTP (HTTPS) знает каждый, так как мы постоянно встречаемся с ним в своих браузерах. Как правило, браузер постоянно запрашивает у сервера, есть ли для него сообщения, а потом получает их. Здесь возможны различные типы запросов (POST, GET, PUT), причём каждый из них имеет своё назначение. Особенности работы хорошо видны на картинке:

1-20219-487645.jpg

А что сокеты?

Сокет для ответа не нуждается в ваших повторяющихся запросах. Сокет в сети работает таким образом, что достаточно лишь один раз выполнить запрос, а потом ждать отклика. И вы можете спокойно «слушать» сервер, который отправит сообщения по мере готовности.

2-20219-310ff7.jpg

Когда используют веб-сокет?

Сокет применяется в том случае, если вы, к примеру, разрабатываете: — чат- и IoT-приложения; — программы, работающие в режиме реального времени; — многопользовательские игры.

Когда нежелательно применять сокет?

Особых противопоказаний в отношении сокета и сокетов нет. Один из недостатков — несовместимость с некоторыми видами браузеров, но этот минус уже практически изжил себя, так как сегодня около 95 % браузеров поддерживают сокеты.

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

Не стоит применять сокет и в REST API, так как будет вполне достаточно стандартных HTTP-запросов (GET, DELETE, POST и PUT). В целом специалисты не рекомендует использовать сокет, если это не риалтайм-приложение, но вопрос о недостатках сокетов требует более детального рассмотрения в рамках отдельной статьи.

От теории к практике

Давайте напишем сокет самостоятельно. Для создания клиента будем использовать язык программирования JavaScript, а для создания сервера — Node.js.

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

Cоздание клиента

 
DOCTYPE html> html> head> title>Пример чата с сокетом/title> /head> body> script> let ws = new WebSocket("ws://localhost:8080"); // выведем новые сообщения в нашу консоль ws.onmessage = (data>) =>  console.log(data); > // отправим сообщение ws.onopen = () => ws.send('Text'); /script> /body> /html> 

Cоздание сервера

 
const WebSocket = require('ws'); // создаём новый сокет-сервер const wss = new WebSocket.Server( port: 8080 >); // отправляем клиентам, если функция `clientValidator` возвращает true. This — это wss. wss.broadcast = function(data, clientValidator = () => true)  this.clients.forEach(client =>  if (clientValidator(client))  client.send(data); > >); > wss.on("connection", ws =>  // событие вызовется, если клиент отправит сообщение ws.on('message', message =>  // отправим сообщение всем за исключением автора wss.broadcast(message, client => client !== ws); >); >); 

И что в итоге?

Что же, давайте продемонстрируем работу созданных нами сокетов:

Есть ли эквивалент сокету в HTTP?

В принципе, есть. Смотрите, HTTP должен проверять канал в сети на наличие новых сообщений. Следовательно, мы можем задействовать dirty check, то есть «грязную проверку». При этом подходе клиент с заданной периодичностью (например, каждые 100 мс) будет проверять, есть ли новые сообщения на сервере. Не вникая в XMLHttpRequest, можно применять библиотеку Axios. Это достаточно понятный и декларативный инструмент. Итак, наш клиент:

 
DOCTYPE html> html> head> title>Обмениваемся сообщениями в режиме онлайн/title> /head> body> script src="https://unpkg.com/axios/dist/axios.min.js">/script> script> let localMessages = []; // первоначальное обновление локального сообщения axios.get('http://localhost:8080/messages') .then((data>) => localMessages = data.messages); // обновление списка сообщений const updateMessages = () =>  axios.get('http://localhost:8080/messages') .then((data>) =>  const difference = data.messages.splice(localMessages.length); difference.forEach(message =>  console.log(message); localMessages.push(message); >); >) .catch(console.log); > // отправка сообщения const sendMessage = text =>  axios.post('http://localhost:8080/messages', text>); > // каждые 100 мс проверяем, присутствуют ли новые сообщения setInterval(updateMessages, 100); /script> /body> /html> 

А теперь посмотрим, что делается на сервере:

 
const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json(), function(req, res, next)  res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next(); >); // в реальном приложении сообщения сохранялись бы в БД либо в JSON-файлах // но не в переменной, которая, как известно, обнуляется в случае остановки сервера let messages = []; // создается новое сообщение app.post('/messages', (req, res) =>  messages.push(req.body.text); res.json( success: true >); >); // получаем все сообщения app.get('/messages', (req, res) =>  res.json( messages >); >); // «слушаем» все запросы на localhost: 8080. app.listen(8080, () => console.log('Слушаю порт 8080')); 

Делаем выводы

Можно с уверенностью заявить, что сокет — довольно перспективная веб-технология, поэтому уже сейчас её использует множество разработчиков. Кроме того, сокеты прекрасно подходят для взаимодействия в реальном времени, включая онлайн-игры.

Работа с сокетами.

Эталонная модель взаимодействия открытых систем описывает уровни сетевого взаимодействия между компьютерами. Данная модель подразумевает семь уровней взаимодействия.

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

Канальный уровень — подразумевает работу с кадрами (frame) обеспечивая взаимодействие на физическом уровне и контроль ошибок. Именно к этому уровню относяться Ethernet, PPPoE и прочие. К этому уровню также относят драйвер сетевой карты, фактически обеспечивающий взаимодействие канального уровня с сетевым.

Сетевой уровень предназначен для определения пути передачи данных. Отвечает за трансляцию логических адресов и имён в физические, определение кратчайших маршрутов, коммутацию и маршрутизацию, отслеживание неполадок и «заторов» в сети. К этому уровню относяться такие протоколы, как IP, RIP и пр.

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

Сеансовый уровень модели обеспечивает поддержание сеанса связи, позволяя приложениям взаимодействовать между собой длительное время. Уровень управляет созданием/завершением сеанса, обменом информацией, синхронизацией задач, определением права на передачу данных и поддержанием сеанса в периоды неактивности приложений. Сюда относяться протоколы PPTP, SOCKS и пр.

Уровень представления обеспечивает преобразование протоколов и кодирование/декодирование данных. Запросы приложений, полученные с прикладного уровня, на уровне представления преобразуются в формат для передачи по сети, а полученные из сети данные преобразуются в формат приложений. На этом уровне может осуществляться сжатие/распаковка или шифрование/дешифрование, а также перенаправление запросов другому сетевому ресурсу, если они не могут быть обработаны локально.

Прикладной уровень — верхний уровень модели, обеспечивающий взаимодействие пользовательских приложений с сетью. К этому уровню относяться такие протоколы, как HTTP, FTP и пр.

Данная выше модель — эталонная. Реальная же модель, на которой работает современный интернет - стек протоколов TCP/IP

Cтек протоколов TCP/IP

Модель OSI являеться эталонной, в то время, как реально в сетях применяеться TCP/IP, который выглядит следующим образом:

  • Прикладной уровень (Application Layer) HTTP ;
  • Транспортный уровень (Transport Layer) TCP ;
  • Межсетевой уровень (Сетевой уровень) (Internet Layer) IP ;
  • Канальный уровень (Network Access Layer) Ethernet .
    • Физический (непосредственно не относится к TCP/IP)

    Программный интерфейс сокет

    Сокет — программный интерфейса для обеспечения обмена данными между процессами. Процессы при таком обмене могут исполняться как на одной ЭВМ, так и на различных ЭВМ, связанных между собой сетью. Сокет — абстрактный объект, представляющий конечную точку соединения.

    Сокеты бывают клиентские и серверные. Клиентские можно сравнить с конечными аппаратами телефонной сети, а серверные — с коммутаторами. Клиентское приложение (например, браузер) использует только клиентские сокеты, а серверное (например, веб-сервер, которому браузер посылает запросы) — как клиентские, так и серверные сокеты.

    Программный интерфейс сокетов в той или иной мере поддерживается всеми современными операционными системами.

    Для взаимодействия между машинами с помощью стека протоколов TCP/IP используются адреса и порты. Адрес представляет собой 32-битную структуру для протокола IPv4, 128-битную для IPv6. Номер порта — целое число в диапазоне от 0 до 65535 (для протокола TCP).

    Эта пара определяет сокет («гнездо», соответствующее адресу и порту).

    В процессе обмена, как правило, используется два сокета — сокет отправителя и сокет получателя. Например, при обращении к серверу на HTTP-порт сокет будет выглядеть так: 194.106.118.30:80, а ответ будет поступать на mmm.nnn.ppp.qqq: xxxxx.

    Каждый процесс может создать «слушающий» сокет (серверный сокет) и привязать его к какому-нибудь порту операционной системы (в UNIX непривилегированные процессы не могут использовать порты меньше 1024).

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

    Каждый сокет имеет свой адрес. ОС семейства UNIX могут поддерживать много типов адресов, но обязательными являются INET-адрес и UNIX-адрес. Если привязать сокет к UNIX-адресу, то будет создан специальный файл (файл сокета) по заданному пути, через который смогут сообщаться любые локальные процессы путём чтения/записи из него. Сокеты типа INET доступны из сети и требуют выделения номера порта.

    Обычно клиент явно «подсоединяется» к слушателю, после чего любое чтение или запись через его файловый дескриптор будут передавать данные между ним и сервером.

    Клиент на socket

    Для работы с сокетами необходимо:

    1. Подключить socket ;
    2. Создать сокет
    3. Присоединиться к серверу (для этого необходимо знать адрес сервера и номер порта, к которому Вы присоединяетесь)
    4. Отправить сообщение. Сообщение должно быть битовым: можно получить из строки при помощи метода encode
    5. Получить ответ
    import socket # Подключаем sock = socket.socket() # Создаём sock.connect(("", "")) # Присоединяемся sock.send("some text data".encode()) # Отправка data = sock.recv(data length in bytes>) # Ответ data = data.decode("utf8") # раскодируем сообщение в строку 

    Задачи

    Подсоединитесь к серверу 51.250.21.231 на порт - 9000 . После того как вы присоединитесь - начинайте общение с сервером:

    • Cервер отправляет вопрос
    • Вы отправляете ответ
    • Cервер отправляет вопрос
    • Вы отправляете ответ
    • . Имя первой задачи — Register

    P.S. Для упрощения работы с сервером целесообразно обработку входящих сообщений вынести в отдельный поток, поскольку сервер периодически будет отправлять сразу по несколько запросов.

    Например, отдельной нитью (Thread) можно запустить следующую функцию

    def listener(sock): while sock.fileno() != -1: data = sock.recv(1000) if len(data) > 0: print(data.decode()) 

    RSA

    RSA — криптографический алгоритм с открытым ключом пригодный и для шифрования и цифровой подписи. Используется в большом числе криптографических приложений, включая TLS/SSL, который и шифрует HTTP соединение, "превращая" его в HTTPS.

    Криптографические системы с открытым ключом используют так называемые односторонние функции, которые обладают следующим свойством:

    • если известно x , то f(x) вычислить относительно просто;
    • если известно y = f(x) , то для вычисления x нет простого (эффективного) пути.

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

    Шифрование и расшифрование

    Предположим, Боб хочет послать Алисе сообщение m.

    Сообщениями являются целые числа в интервале от 0 до n-1 , т.е mZn .

    Сетевые сокеты

    Сетевые сокеты — способ взаимодействия любого приложения с системой на которой оно работает. Сокет — это интерфейс, который по сути представляет собой совокупность адреса в сети и используемого порта. Также часто термин употребляется применительно к Socket API, о котором рассказывается во второй части материала.

    Приложение работает на сервере, подключенном к сети: глобальной или локальной.

    Интерфейс между приложением и хост-машиной (Network-Application Interface) определяет как приложение может использовать сеть.

    Сетевые сокеты

    Сетевые сокеты и клиент серверная модель

    Приложение клиента запрос (к MySQL, например) на определенный сетевой адрес и порт. В примере это localhost и порт 3306 — сервер в свою отвечает приложению.

    К приложению при этом могут обращаться множество клиентов. Запросы и использованием сокета приходят на один и тот же адрес и обрабатываются одним пакетом. Так работает клиент-серверая модель взаимодействия.

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

    Один сервис может работать и по Unix сокету и по сетевому сокету в зависимости от настроек.

    Примером может быть php-fpm.

    Сервис часто запускается на unix сокете и в настройках nginx настраивается проксирование на него. Столь же часто она запускается на адресе 127.0.0.1 и порту 9000 или любом другом.

    Чтобы написать приложение, которое могло бы обслуживать множество клиентов (последовательно и параллельно) нужно сокет API

    Socket API — интерфейс используемый всеми интернет приложениями.

    Socket API при соединении 2-х приложений может работать с потоками и с датаграммами :

    • потоки — отправка потока байтов с гарантированной доставкой
    • датаграммы — отдельные сообщения без гарантии доставки

    Сетевой сокет — комбинация IP адреса и номера порта, которые представляют собой способ адресации.

    Адрес и порт обеспечивают нормальное взаимодействие большого количества приложений в рамках одной системы.

    Один сокет не может использовать два приложения одновременно или два экземпляра одного приложения.

    Вызовы в Socket API

    Socket API

    SOCKET — вызов создает структуру

    BIND — связывает локальный адрес с сокетом

    LISTEN — заявляет о готовности установить соединение

    ACCEPT — принимает входящее соединение

    CONNECT — пробует установить соединение

    SEND — отправляет данные в рамках соединения

    RECEIVE — принимает информацию в рамках соединения

    CLOSE — прерывает соединение

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

    Что такое сокет в сети

    Для обеспечения сетевых коммуникаций используются сокеты. Сокет это конечная точка сетевых коммуникаций. Каждый использующийся сокет имеет тип и ассоциированный с ним процесс. Сокеты существуют внутри коммуникационных доменов. Домены это абстракции, которые подразумевают конкретную структуру адресации и множество протоколов, которое определяет различные типы сокетов внутри домена. Примерами коммуникационных доменов могут быть: UNIX домен , Internet домен , и т.д.

    В Internet домене сокет - это комбинация IP адреса и номера порта, которая однозначно определяет отдельный сетевой процесс во всей глобальной сети Internet. Два сокета, один для хоста-получателя, другой для хоста-отправителя, определяют соединение для протоколов, ориентированных на установление связи, таких, как TCP.

    • Создание сокета
    • Привязка к локальным именам
    • Установление связи
    • Передача данных
    • Закрывание сокетов
    • Пример функции, для установления WWW коннекции

    Создание сокета

    Для создания сокета используется системный вызов socket.

    s = socket(domain, type, protocol);

    Этот вызов основывается на информации о коммуникационном домене и типе сокета. Для использования особенностей Internet, значения параметров должны быть следующими: communication domain - AF_INET (Internet протоколы). type of the socket - SOCK_STREAM; Этот тип обеспечивает последовательный, надежный, ориентированный на установление двусторонней связи поток байтов.

    Выше был упомянут сокет с типом stream. Краткое описание других типов сокетов приведено ниже:

    Datagram socket - поддерживает двусторонний поток данных. Не гарантируется, что этот поток будет последовательным, надежным, и что данные не будут дублироваться. Важной характеристикой данного сокета является то, что границы записи данных предопределены. Raw socket - обеспечивает возможность пользовательского доступа к низлежащим коммуникационным протоколам, поддерживающим сокет-абстракции. Такие сокеты обычно являются датаграм- ориентированными.

    Функция socket создает конечную точку для коммуникаций и возвращает файловый дескриптор, ссылающийся на сокет, или -1 в случае ошибки. Данный дескриптор используется в дальнейшем для установления связи.

    Для создания сокета типа stream с протоколом TCP , обеспечивающим коммуникационную поддержку, вызов функции socket должен быть следующим:

    s = socket(AF_INET, SOCK_STREAM, 0);

    Привязка к локальным именам

    Сокет создается без имени. Пока с сокетом не будет связано имя, удаленные процессы не имеют возможности ссылаться на него и, следовательно, на данном сокете не может быть получено никаких сообщений. Коммуникационные процессы используют для данных целей ассоциации. В Internet домене ассоциация складывается из локального и удаленного адреса и из локального и удаленного порта. В большинстве доменов ассоциация должна быть уникальной.

    В Internet домене связывание сокета и имени может быть весьма сложным, но, к счастью, обычно нет необходимости специально привязывать адрес и номер порта к сокету, так как функции connect и send автоматически свяжут данный сокет с подходящим адресом, если это не было сделано до их вызова.

    Для связывания сокета с адресом и номером порта используют системный вызов bind:

    bind(s, name, namelen);

    Привязываемое имя (name) это строка байт переменной длины, которая интерпретируется поддерживаемым протоколом. Интерпретация может различаться в различных коммуникационных доменах.

    Установление связи

    Со стороны клиента связь устанавливается с помощью стандартной функции connect:

    error = connect(s, serveraddr, serveraddrlen);

    которая инициирует установление связи на сокете, используя дескриптор сокета s и информацию из структуры serveraddr , имеющей тип sockaddr_in , которая содержит адрес сервера и номер порта на который надо установить связь. Если сокет не был связан с адресом, connect автоматически вызовет системную функцию bind.

    Connect возвращает 0, если вызов прошел успешно. Возвращенная величина -1 указывает на то, что в процессе установления связи произошла некая ошибка. В случае успешного вызова функции процесс может работать с дескриптором сокета, используя функции read и write, и закрывать канал используя функцию close.

    Со стороны сервера процесс установления связи сложнее. Когда сервер желает предложить один из своих сервисов, он связывает сокет с общеизвестным адресом, ассоциирующимся с данным сервисом, и пассивно слушает этот сокет. Для этих целей используется системный вызов listen:

    error = listen(s, qlength);

    где s это дескриптор сокета, а qlength это максимальное количество запросов на установление связи, которые могут стоять в очереди, ожидая обработки сервером; это количество может быть ограничено особенностями системы.

    Когда сервер получает запрос от клиента и принимает решение об установлении связи, он создает новый сокет и связывает его с ассоциацией, эквивалентной 'слушающему сокету'. Для Internet домена это означает тот же самый номер порта. Для этой цели используется системный вызов accept:

    newsock = accept(s, clientaddr, clientaddrlen);

    Сокет, ассоциированный клиентом, и сокет, который был возвращен функцией accept, используются для установления связи между сервером и клиентом.

    Процесс установления связи показан на рисунке 1.

    Рис. 1: Взаимодействие клиента и сервера

    Передача данных

    Когда связь установлена, с помощью различных функций может начаться процесс передачи данных. При наличии связи, пользователь может посылать и получать сообщения с помощью функций read и write:

    write(s, buf, sizeof(buf)); read(s, buf, sizeof(buf));

    Вызовы send и recv практически идентичны read и write, за исключением того, что добавляется аргумент флагов.

    send(s, buf, sizeof(buf), flags); recv(s, buf, sizeof(buf), flags);

    • MSG_OOB - Посылать/получать данные, характерные для сокетов типа stream.
    • MSG_PEEK - Просматривать данные без чтения. когда указывается в recv, любые присутствующие данные возвращаются пользователю, но сами данные остаются как "непрочитанные". Следующий read или recv вызванный на данном сокете вернет прочитанные в прошлый раз данные.
    • MSG_DONTROUTE - посылать данные без маршрутизации пакетов. (Используется только процессами, управляющими таблицами маршрутизации.)

    Закрывание сокетов

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

    Если сокет больше не используется, процесс может закрыть его с помощью функции close, вызвав ее с соответствующим дескриптором сокета:

    Если данные были ассоциированы с сокетом, обещающим доставку (сокет типа stream), система будет пытаться осуществить передачу этих данных. Тем не менее, по истечении довольно таки длительного промежутка времени, если данные все еще не доставлены, они будут отброшены. Если пользовательский процесс желает прекратить любую передачу данных, он может сделать это с помощью вызова shutdown на данном сокете для его закрытия. Вызов shutdown вызывает "моментальное" отбрасывание всех стоящих в очереди данных. Формат вызова следующий:

    • 0 - если пользователь больше не желает читать данные
    • 1 - если данные больше не будут посылаться
    • 2 - если данные не будут ни посылаться ни получаться

    Пример функции, для установления WWW коннекции

    /* ----------------------------------------------------------- MakeConnection Function allocates a socket and estabishes a connection with remote host. Default port number 80. Input : WWW server name (with port number, if it is not 80) Output : file descriptor on success -1 on error ----------------------------------------------------------- */ int MakeConnection(unsigned char* ServerName)< int s; struct sockaddr_in ssin; struct hostent* hp; int PortNum; unsigned char strHlp[STRNGLEN], *pch; /* use default port number - 80 or specific number from the server name */ strcpy(strHlp,ServerName); pch = strchr(strHlp,':'); if(pch==NULL)< PortNum = 80; >else < pch[0] = '\0'; pch++; PortNum = atoi(pch); if(PortNum==0)< PortNum = 80; >> /* get host by name - resolve host name into IP address */ if( (hp=gethostbyname(strHlp)) == NULL ) < return -1; >bzero(&ssin, sizeof(ssin)); bcopy(hp->h_addr, &ssin.sin_addr, hp->h_length); ssin.sin_family = hp->h_addrtype; ssin.sin_port = htons(PortNum); /* allocate a socket */ if((s=socket(AF_INET, SOCK_STREAM, 0))==-1) < return -1; >/* make a connection */ if(connect(s, &ssin, sizeof(ssin), 0)==-1) < return -1; >return s; /* socket descriptor */ >

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *