Skip to content

SL:NET Basics (Russian)

Pavel Akulichev edited this page Oct 17, 2020 · 5 revisions

Основы SL:NET

В данном материале представлены основные аспекты работы с модулем SL:NET, а также некоторые пояснения, которые помогут разработчикам разобраться в сути работы данной библиотеки и без каких-либо трудностей использовать её в своих проектах, а также для разработки стронных продуктов.

SL:NET (Simple Lua Network) — модуль, работающий на базе LuaJIT 2.1.0 и библиотеки LuaSocket, выполняющий функцию упрощения разработки программного обеспечения, в котором необходимо использование сокетов. Для своей работы SL:NET использует интернет-протокол UDP (User Datagram Protocol), это обусловлено тем, что SL:NET в первую очередь используется для развёртывания скоростных сетей, например, для написания сетевых игр.

Несмотря на то, что SL:NET использует UDP, который не гарантирует доставку пакетов в целости и сохранности, в SL:NET имеются встроенные системы идентификации и верификации пакетов, а также множество приоритетов доставки: насколько тщательно проверять доставку или не проверять вообще зависит лишь от того, какой приоритет вы устанавливаете при отправке пакета. Возможно задать такие настройки, что пакет будет отправляться до тех пор, пока он не будет получен, либо пока клиент не будет отключен от сервера, то есть практически бесконечное количество раз. Стоит отметить, что может произойти тайм-аут.

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

В SL:NET присутствуют две основные части: сервер и клиент. Первый выполняет роль ретранслятора для всех клиентов, а также своеобразным обработчиком. Клиенты же получают информацию от сервера и на её основе выполняют какие-либо действия. За примером далеко ходить не нужно: мы все прекрасно понимаем как работают сетевые игры с выделенным сервером, а игроки на этих серверах - клиенты. В SL:NET всё работает по абсолютно тому же принципу.

Модуль SL:NET легко поддается кастомизации: вы можете выставлять такие настройки, которые нужны конкретно вам, и для этого не нужно изменять исходный код самого модуля, всё можно делать из своих кастомных скриптов. Давайте для начала рассмотрим два простых примера: сервер и клиент, работающие вместе.

local slnet = require 'slnet'
local server = slnet.server()

server.max_clients_connected = 10
server.connection_timeout = 60

server:bind('*', 8888)

function server.on_receive_packet(id, bs, priority, address, port)
  if id == 1 then
    server:send_packet(1, nil, SLNET_SYSTEM_PRIORITY, address, port)
  elseif id == 2 then
    print(bs:read('string', bs:read('unsigned char')))
    server:send_packet(2, nil, SLNET_SYSTEM_PRIORITY, address, port)
  end
end

while true do
  server:check_updates()
end

Выше представлен пример сервера. Общая суть работы следующая: сервер автоматически выбирает себе IP-адрес и начинает слушать порт 8888. Получая какой-либо пакет с идентификатором 1, сервер отправляет в ответ пакет с тем же идентификатором, а получая в ответ идентификатор 2, он читает полученную вместе с пакетом информацию и выводит строку с определенной длиной. Всё это возможно при помощи SL:NET BitStream, прочитать о котором можно на специальной странице в этой вики. Мы также видим и другие элементы кода, давайте разберемся с ними поочередно, начнем с самого верха кода.

local server = slnet.server()

При помощи функции slnet.server() (также есть функция slnet.client()) мы как бы сообщаем SL:NET, что нам необходима реализация конкретно сервера (а в случае с slnet.client() - клиента), и нам нужно загрузить конфигурацию конкретно для сервера. Использовать функцию можно в любой части вашего скрипта.

server.max_clients_connected = 10
server.connection_timeout = 60

Эти два параметра: max_clients_connected и connection_timeout являются частью конфигурации SL:NET модуля, а если быть точнее, то того объекта server, который мы создали при помощи функции slnet.server(). При этом первый параметр - исключительно серверная настройка, она регулирует максимальное количество клиентов, которые могут подключиться к серверу единовременно. А второй параметр - общий как для клиента, так и для сервера. Он контролирует максимальное время в секундах через которое, в случае отсутствия входящих пакетов, будет произведен обрыв соединения к клиентом или сервером.

function server.on_receive_packet(id, bs, priority, address, port)

Данная функция не вызывается вами в коде, она выполняет роль Event Handler и вызывается SL:NET при получении входящего пакета. Содержит такие аргументы как идентификатор пакета, BitStream, значение типа BOOL о приоритетности пакета, а также адрес и порт отправителя. Функция не является обязательной для объявления, модуль может работать и без неё, но у вас не будет возможности обрабатывать что-либо без этой функции.

server:send_packet(1, nil, SLNET_SYSTEM_PRIORITY, address, port)

Этот метод используется для отправки собственных пакетов. Принимает следующие аргументы: идентификатор пакета, BitStream, приоритет отправки (по умолчанию SLNET_SYSTEM_PRIORITY), адрес получателя и порт получателя. В случае клиента адрес и порт получателя не указывается.

server:check_updates()

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

function server.on_any_event(event, data)

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

А теперь небольшой пример клиента, разбирать его не будем, потому что там всё по аналогии с сервером.

local slnet = require 'slnet'
local client = slnet.client()

client.connection_timeout = 20
client:connect('127.0.0.1', 8888) -- [1 ARG] адрес сервера

client:send_packet(1, nil) -- отправка пакета со стороны клиента

function client.on_receive_packet(id, bs, priority, address, port)
  if id == 1 then
    local bitstream = BitStream()
    bitstream:write('unsigned char', 4)
    bitstream:write('string', 'ABCD')
    client:send_packet(2, bitstream)
  end
end

while true do
  client:check_updates()
end

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

local connection_status = client.status
if connection_status == SLNET_CONNECTED then
  print('You are connected!')
elseif connection_status == SLNET_CONNECTING then
  print('You are connecting...')
else
  print('You are disconnected!')
end
Clone this wiki locally