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()
должна принимать следующие входные аргументы:
hServerSocket:= SysSockCreate(SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP,ADR(result));
// hServerSocket – системный идентификатор сокета RTS_IEC_HANDLE, создаваемый SysSockCreate;
// SOCKET_AF_INET задает сетевой протокол IPv4;
// SOCKET_STREAM определяет тип создаваемого сокета, в данном случае потоковый режим (TCP);
// SOCKET_IPPROTO_TCP определяет протокол сокета, в данном случае TCP;
// ADR(result) – указатель на системный идентификатор (handle) результата функции
// result имеет тип структуры RTS_IEC_RESULT, и содержит коды ошибок, возникающих
// при работе с сокетами (рис. 5).
|
Далее сокет сервера привязывается к определенному IP-адресу и порту с помощью функции SysSockBind()
. Для привязки к определенному IP-адресу функция SysSockBind()
ссылается на структуру SOCKADDRESS
, в которой хранятся заданный адрес сокета для привязки.
result:= SysSockBind(hServerSocket, ADR(serverAddress), SIZEOF(serverAddress));
// Дескриптор серверного сокета hServerSocket связывается с адресом сокета serverAddress,
// описываемой структурой SOCKADDRESS
|
После успешной привязки к адресу функция SysSockListen()
включает прослушивание входящих соединений (ожидание подключений клиентов к серверу). Функцией SysSockListen()
также определяется максимальное количество подключений к серверу. Например, если максимальное количество подключений равно 3, и если 3 клиента уже подключились к серверу, то 4-тому будет отказано в подключении.
result:= SysSockListen(hServerSocket, maxConnections);
// где hServerSocket – системный идентификатор серверного сокета;
// maxConnections - максимальное количество входящий соединений;
|
Обмен данными
После того как сервер включает режим прослушивания, он переходит в рабочий режим и ждет входящие соединения от клиентов. Как только клиент подключается к сокету сервера, с помощью функции SysSockAccept()
создается системный идентификатор клиентского сокета hclientSocket
и соединение считается открытым:
hclientSocket:= SysSockAccept(hServerSocket, ADR(clientAddress), ADR(adressSize),ADR(result));
// hclientSocket - системный идентификатор клиентского сокета;
// clientAddress – структура SOCKADDRESS, где хранится адрес клиента;
// ADR(adressSize) – указатель на размер структуры SOCKADDRESS.
|
Серверный сокет принимает сообщения с помощью функции SysSockRecv()
:
bytesRead:= SysSockRecv(hClientSocket, ADR(recvBuffer), SIZEOF(recvBuffer), 0, ADR(result));
// bytesRead – количество полученных байт сообщения. В случае ошибки возвращается 0;
// hClientSocket – системный идентификатор клиентского сокета;
// ADR(recvBuffer) указатель на переменную, в которую сохраняется принимаемое сообщение;
// SIZEOF(recvBuffer) – размер принимаемого сообщения;
// Вместо 0 могут быть установлены дополнительные опции для приема сообщений
// (подробнее в описании функции в библиотеке SysSocket);
// ADR(result) – указатель на идентификатор результата.
|
Затем отправляет данные с помощью функции SysSockSend()
:
bytesSend:= SysSockSend(hClientSocket, ADR(sendBuffer), SIZEOF(sendBuffer), 0, ADR(result));
// bytesSend – количество отправленных байт. В случае ошибки возвращается 0;
// hClientSocket – системный идентификатор клиентского сокета;
// ADR(sendBuffer) указатель на переменную, которая содержит отправляемое сообщение;
// SIZEOF(sendBuffer) – размер принимаемого сообщения;
// Вместо 0 могут быть установлены опции приема сообщений;
// ADR(result) – указатель на идентификатор результата.
|
Обработка новых подключений
После успешных приема и передачи данных может быть реализовано несколько вариантов поведения программы:
- 1. Программа может закрыть клиентское соединение. В таком случае, в следующих циклах программы сервер будет ожидать подключения с новым клиентом. Такой режим работы не является эффективным, так как контроллеру придется во время каждого цикла закрывать клиентское соединение и подключать нового (или того же самого что и в предыдущем цикле) клиента.
- 2. Программа может не закрывать клиентский сокет, а сохранить установленное соединение. В таком случае, один раз установив соединение, клиент будет постоянно отправлять и получать данные от сервера. Такой режим работы более эффективный, но может возникнуть ситуация, когда все клиентские соединения будут заняты, и новый клиент не сможет подключиться к серверу. Решить данную ситуацию можно различными способами. Один из вариантов – следить за последним временем активности клиентских сокетов, и отключать самое старое соединение в случае, если в очереди обнаружился новый клиент (рис. 4).
Рис. 4. Обработка подключения нового клиента
Закрытие соединения
В рабочем режиме работы серверный сокет всегда остается открытым. Закрытие серверного сокета может происходить при внешнем событии, или при возникновении критических ошибок. Ошибки при создании и работе сокетов отображаются в системном идентификаторе result, который имеет тип структуры RTS_IEC_RESULT. Обозначение кодов ошибок описано в системной библиотеке CmpErrors Interfaces в глобальных константах Errors (рис. 5).
Для закрытии сокетного соединения используется фукнция SysSockClose()
:
SysSockClose(hServerSocket);
// где hServerSocket – системный идентификатор серверного сокета
|
Рис. 5. Расшифровка кодов ошибок работы сокетов
Клиентский TCP сокет
Схема работы клиентского сокета отображена на рисунке 3 справа.
Инициализация клиента
Функция SysSockCreate()
создает системный идентификатор сокета. Также, как и для сервера, для клиента необходимо создать потоковый сокет:
hClientSocket:=SysSockCreate(SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP,ADR(result));
// hClientSocket – системный идентификатор клиентского сокета;
// SOCKET_AF_INET задает сетевой протокол IPv4;
// SOCKET_STREAM определяет тип сокета, в данном случае потоковый сокет (TCP);
// SOCKET_IPPROTO_TCP определяет протокол сокета, в данном случае TCP;
// ADR(result) – указатель на системный идентификатор (handle) результата функции.
|
Зная IP-адрес и порт сервера, клиент с помощью SysSockConnect()
подключается к серверному сокету:
result:=SysSockConnect(hClientSocket, ADR(clientAddress), SIZEOF(clientAddress));
// hClientSocket – системный идентификатор клиентского сокета;
// ADR(clientAddress) – указатель на структуру SOCKADDRESS с адресом сокета сервера;
// SIZEOF(clientAddress) – размер структуры адреса сокета. |
Обмен данными
Обмен данными между клиентом и с помощью функций SysSockSend()
и SysSockRecv()
:
bytesSend:= SysSockSend(hClientSocket, ADR(sendMessage), SIZEOF(sendMessage), 0,ADR(result));
bytesRecv:=SysSockRecv(hClientSocket, ADR(recvMessage), SIZEOF(recvMessage), 0, ADR(result));
// hClientSocket – системный идентификатор клиентского сокета;
// ADR(sendMessage/recvMessage) - указатель на отправляемое/полученное сообщение;
// SIZEOF(sendMessage/recvMessage), размер отправленного/полученного сообщения;
// ADR(result) – указатель на системный идентификатор (handle) результата функции. |
Закрытие соединения
После обмена данными сокет может быть закрыт с помощью с помощью SysSockClose()
:
SysSockClose(hClientSocket);
// где hClientSocket – системный идентификатор серверного сокета. |
Однако, с точки зрения циклического обмена данными реального времени, каждый раз закрывать и открывать сокет заново неэффективно. Поэтому после успешной установки соединения обмен данными осуществляется в бесконечном цикле.
Особенности сокетов TCP
Использование TCP сокетов позволяет приложениям клиента и сервера обмениваться данными почти прозрачно, не заботясь о поддержании сетевого соединения, доставке пакетов по сети, порядке передачи пакетов и буферизации. TCP сокеты гарантируют доставку сообщений и правильный порядок пакетов, а также пересылают пакеты повторно, если подтверждение о передаче не приходит в течение определенного промежутка времени. Таким образом, использовать TCP сокеты уместно там, где необходима гарантированная доставка данных сетевыми средствами.
Несмотря на многие преимущества, TCP сокеты имеют и негативные стороны. Например, необходимость поддержания TCP-соединения уменьшает пропускную способность обмена данными в распределенных системах. Также, в системах обмена данными реального времени повторная передача потерянных пакетов может привести к тому, что система получит данные, которые утратили свою актуальность.
Наверх