Документация Engee

Пакет поддержки Sockets

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

В контексте Engee сырой сокет работает на стороне клиентской программы пользователя через подсистему Engee.Интеграции, а в самом Engee вы получаете результаты обработки данных с клиентской программой или отправляете данные на клиентскую программу в одностороннем/двустороннем порядке.

Чтобы начать работу с пакетом поддержки сокетов, установите подсистему Engee.Интеграции: запустите клиентскую программу и укажите URL сервера Engee (см. инструкцию).

Основные методы

Ниже перечислены ключевые методы:

  • Sockets.Socket(family="AF_INET", socket_type="SOCK_STREAM", socket_descriptor=0) — конструктор (по умолчанию TCP/IPv4).

  • Sockets.bind(device, host::String, port::Int64) — привязка к адресу/порту.

  • Sockets.listen(device, backlog::Int64) — режим прослушивания входящих подключений (для серверов).

  • Sockets.accept(device) → Int64 — ожидает клиента, возвращает дескриптор[1] нового сокета.

  • Sockets.connect(device, host::String, port::Int64) → Int64 — подключение к удаленному серверу (для клиентов).

  • Sockets.send(device, message::Vector{UInt8}) — отправка байтов.

  • Sockets.receive(device, bufsize::Int64) → Union{Nothing, ResultUIntList} — чтение в блокирующем режиме.

  • Sockets.is_open(device, socket_descriptor::Int64) → Bool — проверка, открыт ли сокет.

  • Sockets.close(device) — закрытие сокета и освобождение ресурсов.

Пошаговый пример работы с Sockets

Рассмотрим пример обмена данными между двумя UDP-сокетами на одном компьютере (localhost). В этом примере:

  • Сокет 1 выступает в роли сервера: он привязывается к конкретному порту и ожидает данные;

  • Сокет 2 выступает в роли клиента: он подключается к порту сокета 1 и отправляет сообщения.

    1. Создание первого сокета (сервер) — создайте сокет, указав семейство адресов AF_INET (IPv4) и тип SOCK_DGRAM (UDP):

      socket_1 = SOCKET.Socket("AF_INET", "SOCK_DGRAM")
    2. Привязка первого сокета к адресу и порту — привяжите сокет к адресу локальной машины (127.0.0.1) и порту 8888. Теперь сокет «слушает» этот порт:

      socket_1.bind("127.0.0.1", 8888)
    3. Создание второго сокета (клиент) — создайте второй сокет с теми же параметрами:

      socket_2 = SOCKET.Socket("AF_INET", "SOCK_DGRAM")
    4. Подключение второго сокета к серверу — подключите клиентский сокет к адресу и порту, на котором «слушает» серверный сокет:

      socket_2.connect("127.0.0.1", 8888)
    5. Цикл отправки и проверки сообщений — отправьте 1000 сообщений из второго сокета и сразу же примите их в сокете один, проверяя корректность доставки:

      const num_messages = 1000
      
      for i in 1:num_messages
          # Формируем текстовое сообщение
          message = "Message $i"
          # Преобразуем сообщение в массив байт
          byte_array = collect(codeunits(message))
          # Отправляем массив байт через сокет 2
          socket_2.send(byte_array)
      
          # Принимаем данные (максимум 1000 байт) на сокете 1
          res_for_recv = socket_1.receive(1000).data
          # Проверяем, что полученные данные совпадают с отправленными
          @assert res_for_recv == byte_array
      end
      Тип SOCK_DGRAM (UDP) не гарантирует доставку и порядок пакетов, но в рамках одного компьютера (localhost) обмен данными происходит практически без потерь, что делает пример стабильным.
    6. Закрытие сокетов и освобождение ресурсов — после завершения обмена закройте оба сокета:

      socket_1.close()
      socket_2.close()

Полезные ссылки


1. Дескриптор — это целое число, которым ОС помечает открытый ресурс: сокет, файл, подключение и т.д. Программе не нужно знать, что внутри; она передает этот номер в функции чтения/записи/закрытия, а ОС по нему находит нужный ресурс. Если дескриптор закрыт или недействителен, то операции с ним не работают.