что такое tcp сокет
Основы программирования TCP-сокетов на Java
Клиент-серверная архитектура — наиболее распространенная структура приложений в Интернете. В этой архитектуре клиенты (т.е. персональные компьютеры, устройства Интернета вещей и т. д.) сначала запрашивают ресурсы с сервера. Затем сервер отправляет обратно соответствующие ответы на запросы клиентов. Чтобы это произошло, должен быть какой-то механизм, реализованный как на стороне клиента, так и на стороне сервера, который поддерживает эту сетевую транзакцию. Этот механизм называется коммуникацией посредством сокетов.
Почти каждое приложение, которое полагается на сетевые операции, такие как извлечение данных с удаленных серверов и загрузка файлов на сервер, широко использует сокеты “под капотом”. Несколько примеров таких приложений — браузеры, чат-приложения и одноранговые сетевые приложения.
В этой статье мы более подробно рассмотрим сокеты и простую клиент-серверную реализацию с использованием сокетов в Java.
Примечание: существует два типа сокетов: TCP и UDP. Поскольку большинство сетевых приложений используют TCP, здесь я буду говорить только о TCP-сокетах и их реализации.
Что такое сокет?
Сокет — это программная (логическая) конечная точка, устанавливающая двунаправленную коммуникацию между сервером и одной или несколькими клиентскими программами. Сокет — это нечто “программное”. Другими словами, сокет не существует на физическом уровне. Прикладное программное обеспечение определяет сокет так, чтобы он использовал порты на основном компьютере для его реализации. Это позволяет программистам комфортно работать с низкоуровневыми деталями сетевых коммуникаций, такими как порты, маршрутизация и т. д., внутри прикладного кода.
Как работают сокеты?
TCP-сокет устанавливает связь между клиентом и сервером в несколько этапов.
На каждой из перечисленных выше стадий коммуникации сокетов “под капотом» происходит много всего сложного. Однако этих знаний вполне достаточно для понимания и демонстрации того, как работает коммуникация посредством TCP-сокетов.
К настоящему времени мы уже достаточно знаем о TCP-сокетах. Давайте теперь посмотрим на них в действии.
Реализация коммуникации посредством TCP-сокетов в Java
Давайте посмотрим, как мы можем реализовать коммуникацию сокетов в Java. Мы сейчас напишем две Java-программы. Одной будет программа, запущенная на сервере, а другой — клиентская программа, которая будет взаимодействовать с сервером.
Реализация серверного сокета
Теперь давайте создадим клиент для взаимодействия с серверным сокетом, созданным выше.
Реализация клиентского сокета
Показанная выше программа действует как клиент, создавая соединение с серверным сокетом. После подключения клиент получает отправленные сервером данные. Входной поток соединяется с буфером, используя BufferedReader для хранения полученных данных, так как мы не можем быть уверены, что данные будут использоваться сразу же после получения. Затем мы считываем данные из буфера и выводим их в консоль.
Запуск программ
Сначала запустите серверную Java-программу, а затем клиентскую Java-программу (потому что сервер уже должен работать для подключения клиента). Вы увидите Received data: Java Revisited в терминале, где работает клиентская программа. Вот что здесь произошло: серверная программа отправила данные клиенту по запросу, а клиентская программа вывела их на терминал.
В этой статье мы обсудили, что такое сокеты и Java-реализация связи TCP-сокетов.
TCP и UDP сокеты в CODESYS V3
CPM723-01: Рекомендации к применению
Сопроводительные документы
Сокеты и стек протоколов TCP/IP
Рис. 1. Пример протоколов стека TCP/IP в соответствии с моделью OSI
В распределенных системах управления обмен данными является одним из ключевых моментов работы системы. Контроллер CPM723-01 позволяет отправлять и получать данные по промышленному протоколу Modbus TCP на базе протокола TCP/IP с использованием двух портов Ethernet и по протоколу Modbus RTU/ACSII на базе последовательных сетей RS-485/ RS-232 с помощью коммуникационных модулей NIM741/NIM742. Кроме того, система исполнения контроллера CPM723-01 поддерживает механизм сетевого обмена данными между контроллерами, принадлежащими одной подсети, средствами специального протокола прикладного уровня CODESYS V3.
Иногда возникает необходимость использовать протоколы низкого уровня, которые позволяют обмениваться большим количеством сообщений с помощью стека TCP/IP. Также, на базе них можно создавать протоколы более высокого уровня модели OSI (рис. 1).
Взаимодействие между устройствами в рамках стека TCP/IP осуществляется с помощью связки IP адреса и порта.
Для заданияIP адресав настоящее время чаще всего используется протокол IPv4. Для него IP-адрес записывается в виде 32-битной формы, представляемой в символьной форме mmm.nnn.ppp.qqq: адрес, разбитый на четыре поля, разделённых точками, по одному байту в поле, например, 192.168.102.101. Номер порта задается в диапазоне от 0 до 65535.
Пара адрес и порт образует сокет (с английского socket – «гнездо»). Сокет – является программным интерфейсом, который обеспечивает обмен данными между устройствами на низком уровне (рис. 2).
Рис. 2. Общение с помощью сокетов.
Протокол TCP/IP основывается на соединениях, устанавливаемых между двумя компьютерами, обычно называемых клиентом и сервером. Поэтому, различают сокет клиента и сокет сервера. Для организации общения клиент должен знать IP адрес и номер порта сервера, по которым он подключается к удаленному устройству. в рамках стека протоколов TCP/IP различают два типа сокетов TCP и UDP. Также, TCP сокеты называют потоковыми, а UDP – датаграммными.
Протокол TCP/IP основывается на соединениях, устанавливаемых между двумя компьютерами, обычно называемых клиентом и сервером. Поэтому, различают сокет клиента и сокет сервера. Для организации общения клиент должен знать IP-адрес и номер порта сервера, по которым он подключается к удаленному устройству. в рамках стека протоколов TCP/IP различают два типа сокетов TCP и UDP. Также, TCP сокеты называют потоковыми, а UDP – датаграммными.
TCP сокеты
TCP сокеты используют TCP-соединения, в которых на транспортном уровне (рис. 1) обеспечивается надёжная доставка данных. TCP протокол отвечает за установление и поддержание соединения, сегментацию, доставку и буферизацию данных, упорядочивание и избавление от дублированных TCP-сегментов данных, контроль ошибок и скорости передачи. Схема работы простого TCP сокета представлена на рисунке 3.
Для удобства в качестве функций, указанных на диаграмме, используются функции, из системной библиотеки SysSocket 3.х.x.x, которая позволяет создавать сокеты на устройствах, поддерживающих платформу CODESYS V3 в том числе на контроллере CPM723-01 модульной линейки Fastwel I/O.
Cерверный TCP сокет
Рассмотрим работу серверного сокета (рис. 3). Будем считать, что существует отдельная программа, исполняемая в контроллере, которая организует обмен данными с помощью сокетов.
Рис. 3. Работа простого TCP сокета
Инициализация сокета
При старте программы происходит инициализация сервера. С помощью функции SysSockCreate() создается системный идентификатор (handle) сокета. Данная функция в качестве входных параметров принимает аргументы, задающие тип и протокол сокета. Для использования TCP протокола функция SysSockCreate() должна принимать следующие входные аргументы:
После успешной привязки к адресу функция SysSockListen() включает прослушивание входящих соединений (ожидание подключений клиентов к серверу). Функцией SysSockListen() также определяется максимальное количество подключений к серверу. Например, если максимальное количество подключений равно 3, и если 3 клиента уже подключились к серверу, то 4-тому будет отказано в подключении.
Обмен данными
После того как сервер включает режим прослушивания, он переходит в рабочий режим и ждет входящие соединения от клиентов. Как только клиент подключается к сокету сервера, с помощью функции SysSockAccept() создается системный идентификатор клиентского сокета hclientSocket и соединение считается открытым:
Серверный сокет принимает сообщения с помощью функции SysSockRecv() :
Затем отправляет данные с помощью функции SysSockSend() :
Обработка новых подключений
После успешных приема и передачи данных может быть реализовано несколько вариантов поведения программы:
Рис. 4. Обработка подключения нового клиента
Закрытие соединения
В рабочем режиме работы серверный сокет всегда остается открытым. Закрытие серверного сокета может происходить при внешнем событии, или при возникновении критических ошибок. Ошибки при создании и работе сокетов отображаются в системном идентификаторе result, который имеет тип структуры RTS_IEC_RESULT. Обозначение кодов ошибок описано в системной библиотеке CmpErrors Interfaces в глобальных константах Errors (рис. 5).
Для закрытии сокетного соединения используется фукнция SysSockClose() :
Рис. 5. Расшифровка кодов ошибок работы сокетов
Клиентский TCP сокет
Схема работы клиентского сокета отображена на рисунке 3 справа.
Инициализация клиента
Функция SysSockCreate() создает системный идентификатор сокета. Также, как и для сервера, для клиента необходимо создать потоковый сокет:
Зная IP-адрес и порт сервера, клиент с помощью SysSockConnect() подключается к серверному сокету:
Обмен данными
Обмен данными между клиентом и с помощью функций SysSockSend() и SysSockRecv() :
Закрытие соединения
После обмена данными сокет может быть закрыт с помощью с помощью SysSockClose() :
Однако, с точки зрения циклического обмена данными реального времени, каждый раз закрывать и открывать сокет заново неэффективно. Поэтому после успешной установки соединения обмен данными осуществляется в бесконечном цикле.
Особенности сокетов TCP
Использование TCP сокетов позволяет приложениям клиента и сервера обмениваться данными почти прозрачно, не заботясь о поддержании сетевого соединения, доставке пакетов по сети, порядке передачи пакетов и буферизации. TCP сокеты гарантируют доставку сообщений и правильный порядок пакетов, а также пересылают пакеты повторно, если подтверждение о передаче не приходит в течение определенного промежутка времени. Таким образом, использовать TCP сокеты уместно там, где необходима гарантированная доставка данных сетевыми средствами.
Несмотря на многие преимущества, TCP сокеты имеют и негативные стороны. Например, необходимость поддержания TCP-соединения уменьшает пропускную способность обмена данными в распределенных системах. Также, в системах обмена данными реального времени повторная передача потерянных пакетов может привести к тому, что система получит данные, которые утратили свою актуальность.
UDP сокеты
Все перечисленные недостатки TCP сокетов связаны с особенностью TCP-протокола. Если в системе присутствие данных факторов крайне нежелательно, а гарантированность доставки сообщений не является критичным требованием, то в качестве альтернативы TCP сокетов могут использоваться UDP (датаграммные) сокеты.
UDP сокеты устроены проще, чем TCP. В качестве транспортного уровня используется протокол UDP, который не требует установления соединения и подтверждения приема. Информация пересылается в предположении, что принимающая сторона ее ожидает. Датаграммные сокеты не контролирует ничего, кроме целостности полученных датаграмм. Несмотря на это, UDP сокеты нашли свое применение в системах, где на первом месте стоит именно актуальность данных и их быстрая доставка, а не гарантия доставки каждого сообщения.
Например, сервер в ответ на запросы клиента передает по сети текущие (мгновенные) значения некоторого параметра контролируемого технологического процесса, а клиент формирует управляющий сигнал на основе принятых значений. Если темп опроса сервера клиентом много больше требуемого времени реакции алгоритма управления на изменение значения контролируемого параметра, то потеря одного-двух сообщений от сервера несущественно повлияет на качество формирования управляющего сигнала. В случае использования TCP соединения потерянное сообщение будет автоматически передано повторно, что может привести к получению клиентом неактуального значения контролируемого параметра и к формированию неправильного управляющего сигнала.
Серверный UDP сокет
На рисунке 6 показана схема работы простого UDP сокета.
Инициализация сервера
Также, как в случае TCP сокетов, системный идентификатор UDP сокета создается с помощью функции SysSockCreate() :
Обмен данными
Рис. 6. Схема работы простого UDP сокета.
Закрытие соединения
После отправки данных, сокет сервера снова переходит к функции SysSockRecvFrom() и остается незакрытым.
Но в случае необходимости серверный сокет можно закрыть аналогично TCP сокету:
Клиентский UDP сокет
Клиент UDP работает аналогично клиентскому сокету TCP за исключением использования функций SysSockSendTo() и SysSockRecvFrom() для отправки и получения сообщений.
Инициализация клиента
Функция SysSockCreate() создает системный идентификатор сокета. Также, как и для сервера, для клиента необходимо создать потоковый сокет:
Обмен данными
В отличие от TCP сокетов, при использовании UDP протокола клиентский сокет не устанавливает соединения с сервером, а сразу после создания клиентского сокета переходит к обмену данными с помощью функций SysSockSendTo() и SysSockRecvFrom() :
Закрытие соединения
После обмена данными сокет может быть закрыт с помощью с помощью SysSockClose() :
Однако, с точки зрения циклического обмена данными реального времени, каждый раз закрывать и открывать сокет заново неэффективно. Поэтому после успешной установки соединения обмен данными осуществляется в бесконечном цикле.
Дополнительные настройки сокетов
На рисунке 3 и 6 показана работа простых серверного и клиентского сокетов. Но на деле, такая простая схема имеет некоторые ограничения и недостатки.
Блокирующий режим
Естественно, такое поведение программы не является безопасным, и при циклическом вызове программы в ПЛК может сработать сторожевой таймер или произойти выход в безопасный режим – контроллер будет считать, что программа зависла.
Подключение несколько клиентов
Серверный сокет, работающий согласно схемам на рисунках 3 и 6, подходит для обмена данными в режиме точка-точка, когда существует одно входящее клиентское соединение. В случае если к серверу будет подключаться несколько клиентов, может возникнуть путаница с принимаемыми и отправляемыми сообщениями, а также может возникнуть очередь на ожидание подключения.
Если хотя бы один сокет клиента готов, например, к отправке данных, SysSockSelect() сообщит об этом программе и соединение с данным клиентом будет установлено. Схема работы серверного сокета с использованием SysSockSelect() показана на рисунке 5.
Функция SysSockSelect() является блокирующей, она возвращает управление, если хотя бы один из проверяемых сокетов готов к выполнению соответствующей операции. Но в качестве настройки в функции можно указать интервал времени, по прошествии которого она вернет управление в любом случае.
Рис. 7. Схема работы сокетов с использованием функции SysSockSelect()
Программа сокетов для CPM723
В проектах TCP_UDP_Sockets.project и 2xPLCs_Sockets.project, входящих в комплект поставки программного обеспечения Fastwel I/O, реализованы программы TCP сокетов и UDP сокетов на языках ST и CFC стандарта МЭК 61131-3.
Структура проекта TCP_UDP_Sockets.project указана на рисунке 8. В данном проекте реализовано два проекта для UDP и TCP сокетов, для работы в рамках одного контроллера CPM723-01. В первом проекте CPM723_LOCAL_CFC работа сокетов реализована с помощью функциональных блоков, вызываемых в программах (язык CFC). Во втором проекте CPM723_LOCAL_ST работа сокетов реализована в программах (язык ST).
Рис. 8. Структура проекта TCP_UDP_Sockets.project
В проекте 2xPLCs_Sockets.project реализован пример для двух контроллеров CPM723-01, обменивающихся данными по протоколу TCP. На первом контроллере ClientsTCP реализованы TCP сокеты клиентов, на втором контроллере ServerTCP – TCP сокет сервера. Структура проекта указана на рисунке 9.
Рис. 8. Структура проекта TCP_UDP_Sockets.project
Заключение
Сокеты отвечают за обмен данными между различными устройствами и процессами. На базе обмена данными по сокетам можно создавать протоколы стека TCP/IP более высокого уровня.
TCP сокеты необходимо там, где требуется надежная доставка сообщений, а скорость передачи данных не критична. UDP сокеты лучше всего использовать там, где нужна эффективность на быстрых сетях с короткими соединениями и данные реального времени, а гарантированность доставки сообщений не нужна.
Сокеты
Сокеты объединили в едином интерфейсе потоковую передачу данных подобную каналам pipe и FIFO и передачу сообщений, подобную очередям сообщений в System V IPC. Кроме того, сокеты добавили возможность создания клиент-серверного взаимодействия (один со многими).
Классификация сокетов
Поток байтов без разделения на записи, подобный чтению-записи в файл или каналам в Unix. Процесс, читающий из сокета, не знает, какими порциями производилась запись в сокет пишущим процессом. Данные никогда не теряются и не перемешиваются.
Передача записей ограниченной длины. Записи на уровне интерфейса сокетов никак не связанны между собой. Отправка записей описывается фразой: «отправил и забыл». Принимающий процесс получает записи по отдельности в непредсказуемом порядке или не получает вовсе.
Надёжная упорядоченная передача с делением на записи. Использовался в Sequence Packet Protocol для Xerox Network Systems. Не реализован в TCP/IP, но может быть имитирован в TCP через Urgent Pointer.
Имена сокетов
Имена сокетов на сервере назначаются вызовом bind(), а на клиенте, как правило, генерируются ядром.
TCP/IP
Для передачи данных с помощью семействе протоколов TCP/IP реализованы два вида сокетов Stream и Datagram. Все остальные манипуляции с сетью TCP/IP осуществляются через Raw-сокеты.
API Сокетов
Создание сокета
protocol Поскольку в семействе протоколов TCP/IP протокол однозначно связан с типом сокета, а в домене Unix понятие протокола вообще отсутствует, то этот параметр всегда равен нулю, что соответствует автовыбору.
В домене Unix возможно создание пары соединённых между собой безымянных сокетов, которые буду вести себя подобно неименованному каналу pipe. В отличие от неименованных каналов, оба сокета открыты и на чтение и на запись.
Назначение имени
Для того, чтобы клиенты могли подключаться к серверу, сервер должен иметь заранее известное имя. Вызов bind() обеспечивает назначение имени серверному сокету. Сервер получит имя клиентского сокета в момент соединения (stream) или получения сообщения (datagram), поэтому на клиентской стороне имя сокету, как правило, назначается ядром ОС, хотя и явное присвоение с помощью bind() остаётся доступным.
Соединение с сервером (в основном Stream)
Для сокета типа Stream вызов connect() соединяет сокет клиента с сокетом сервера, создавая поток передачи данных. Адрес сервера servaddr заполняется по тем же правилам, что и адрес, передаваемый в bind().
Для сокета типа Datagram вызов connect() запоминает адрес получателя, для отправки сообщений вызовом send(). Можно пропустить этот вызов и отправлять сообщения вызовом sendto(), явно указывая адрес получателя для каждого сообщения.
Прослушивание сокетов сервером (только Stream)
Вызов listen() на стороне сервера превращает сокет в фабрику сокетов, которая будет с помощью вызова accept() возвращать новый транспортный сокет на каждый вызов connect() со стороны клиентов.
Обработка запроса клиента.
Клиентский connect() будет заблокирован до тех пор, пока сервер не вызовет accept(). accept() возвращает транспортный сокет, который связан с сокетом для которого клиент вызвал connect(). Этот сокет используется как файловый дескриптор для вызовов read(), write(), send() и recv().
В переменную clntaddr заносится адрес подключившегося клиента.
Чтение/запись данных
Для операций чтения-записи данных через сокеты могут применяться стандартные вызовы read() и write(), однако существуют и более специализированные вызовы:
Все вызовы применимы и к потоковым сокетам и к сокетам датаграмм. При попытке прочитать датаграмму в слишком маленький буфер, её хвост будет утерян.
write(fd,buf,size) == send(fd,buf,size,0) == sendto(fd,buf,size,0,NULL,0)
send() может применяться только к тем сокетам, для которых выполнен connect().
sendmsg() и recvmsg() близки к вызовам writev() и readv(), поскольку позволяют одним вызовом отправить/принять несколько буферов данных.
Управление окончанием соединения (в основном Stream)
Вызов close() закрывает сокет и освобождает все связанные с ним структуры данных.
Для контроля над закрытием потоковых сокетов используется вызов shutdown(), который позволяет управлять окончанием соединения.
int shutdown () (int sock, int cntl);
Аргумент cntl может принимать следующие значения:
Для реализации клиент-серверной архитектуры на основе сокетов необходимо предоставить разработчику сервера инструмент для параллельной работы с несколькими клиентами. Возможные варианты:
Последний вариант является наиболее часто используемым в Unix и реализуется вызовами select() и poll().
Вызовы отличаются по формату параметров, но эквивалентны по своему назначению. Они приостанавливают выполнение процесса, до появления данных от клиента, появления возможности отправить данные клиенту, появления ошибки приёма-передачи или до истечения таймаута. Если точнее, то для операций чтения-записи проверяется, что они не будут заблокированы.
Реализация этих вызовов позволяет использовать их для отслеживания состояния любых файловых дескрипторов, а не только сокетов.
SELECT
Вызов select() получает три битовых набора флагов (чтение, запись, ошибка) размером с максимальное доступное число открытых файловых дескрипторов. Флаг в какой-то позиции означает что мы наблюдаем за соответствующим файловым дескриптором.
Параметр nfds задает номер максимального выставленного флага и служит для оптимизации.
Для манипуляции флагами используется следующие функции, которые позволяют очистить набор флагов, установить флаг, сбросить флаг, проверить состояние флага:
При изменении состояния каких-либо интересующего нас файловых дескрипторов select() сбрасывает все флаги и выставляет те, которые обозначают, какие события и на каких файловых дескрипторах произошли. Возвращается значение, указывающее сколько флагов возвращено. Если событий не было и возврат из select() произошёл по таймауту, все наборы флагов обнуляются и возвращается ноль.
Таймаут задаётся структурой timeval, содержащей секунды и микросекунды
Поскольку вызов sleep() работает с точностью до секунды, то для приостановки процесса на более короткие промежутки времени часто используют select() с указателями NULL вместо указателей на флаги.
Вызов poll() функционально эквивалентен select. Его параметры как бы «вывернуты наизнанку» по сравнению с select(). Вместо трёх наборов битовых файлов в poll() массив интересующих файловых дескрипторов размером nfds. С каждым файловым дескриптором связаны две переменные: флаги интересующих событий и флаги случившихся событий. Время таймаута задаётся в миллисекундах.
struct pollfd < int fd; /* file descriptor / short events; / requested events / short revents; / returned events */ >;
Битовые флаги в events определяются макросами:
Диаграмма взаимодействия сокетов datagram
Ниже представлена временная диаграмма соединения клиента и сервера через сокет типа Datagram
Сервер | Клиент |
---|---|
Создание сокета socket() | Создание сокета socket() |
Присвоение имени bind() | |
Начало цикла работы с клиентами | |
Прием сообщения с адресом отправителя recvfrom() | Приём сообщения recv() |
Закрытие сокета close() | |
Конец цикла работы с клиентами | |
Закрытие сокета close() |
Диаграмма взаимодействия сокетов stream
Ниже представлена временная диаграмма соединения клиента и сервера через сокет типа Stream