-
Notifications
You must be signed in to change notification settings - Fork 1
SL:NET Basics (Russian)
В данном материале представлены основные аспекты работы с модулем SL:NET, а также некоторые пояснения, которые помогут разработчикам разобраться в сути работы данной библиотеки и без каких-либо трудностей использовать её в своих проектах, а также для разработки стронных продуктов.
Начать стоит с того, что SL:NET - это модуль, который основан про протоколе UDP (User Datagram Protocol). UDP как раз тот протокол, который чаще всего используется при создании продуктов, в которых требуется скорость обработки информации. UDP не имеет схем соединения клиентов с сервером и сервера с клиентами, происходит холостой выброс пакетов с данными в заданном направлении, а поймает его получатель или нет - неизвестно.
Основываясь на этих данных, можно сказать, что первое, что нужно разработать - систему подключения для клиентов и для сервера. Нам нужно понимать и чётко осознавать подключен тот или иной клиент к серверу или нет, но как же это сделать? На самом деле ничего сложного нет, каждый клиент имеет уникальную пару идентификационных данных, повториться которая не может - речь идет про IP и порт, которые клиент использует для получения ответов от сервера. Имея информацию о том, что эти данные уникальные, мы можем использовать их в качестве проверочных при получении и отправке пакетов. Давайте на примере разберемся как можно реализовать такую систему сохранения клиентов в памяти сервера для дальнейшего взаимодействия с тем или иным клиентом.
local Clients = {}
function onReceivePackets(packet_id, bitstream, address, port)
if packet_id == 1 then
-- мы будем считать, что PacketID 1 будет нужен для подключения
for i, v in ipairs(Clients) do
if v.address == address and v.port == port then
return -- отклоняем пакет, потому что такой клиент уже есть
end
end
-- добавляем клиента
table.insert(Clients, {
address = address,
port = port
})
end
endЭтим простым примером мы сделали возможным подключение клиентов, этот пример можно усовершенствовать, добавив отправку ответного пакета с информацией о том было подключение успешным или нет, вносить в таблицу с клиентами не только IP и порт, а ещё и другие данные, но это вы сделаете сами.
Но для того, чтобы наш сервер начал функционировать, нам нужно инициализировать SL:NET и задать ему параметры, присущие серверу. Для этого в SL:NET используется специальная функция SLNetBind, либо метод bind для объекта подключения, получаемого функцией SLNetInit. Инициализацию необязательно производить сразу при старте скрипта, это можно сделать абсолютно в любой момент. В нашем же случае мы сделаем это внутри глобальной функции.
function onReceivePackets(packet_id, bitstream, address, port)
-- любые действия с входящими пакетами
end
function netInit()
netHandle = SLNetInit()
netHandle:bind('*', 4334)
netHandle:setPrefix('TEST')
netHandle:setHook(onReceivePackets)
endФункция netInit теперь может быть вызвана в любом месте кода, но давайте же подробно рассмотрим что делает каждая из использованных мною функций и методов. SLNetInit создает объект подключения, который в дальнейшем будет заполнен либо под сервер, либо под клиент. Этот объект нам будет нужен при вызове почти любой функции SL:NET. Метод bind, как уже было сказано, используется для установки набора IP:PORT для сервера. Первым аргументом был передан специальный символ *, который обозначает, что IP адрес сервера будет выбран компьютером автоматически. Если у вас несколько сетевых адаптеров, то рекомендуется указывать маску подсети вручную, чтобы был использован корректный IP-адрес. Метод setPrefix устанавливает префикс для входящих и исходящих пакетов, это нужно для того, чтобы лишние пакеты, отправленные с других скриптов не обрабатывались вашим сервером или клиентом. Последний метод с названием setHook принимает функцию, которая будет вызываться при получении входящих пакетов, обработать которые можно будет в дальнейшем.
Чтобы сделать подключение к серверу со стороны клиента, нужно немного изменить код, а именно заменить метод bind на метод connect и вместо специального символа * передавать IP-адрес сервера, к которому вы хотите подключиться - внешний, либо локальный в случае, если сервер в вашей подсети. При получении неизвестных ошибок об отключенном SOCKET-объекте рекомендуется проверить введенные данные, возможно они заданы некорректно.
function netInit()
netHandle = SLNetInit()
netHandle:connect('127.0.0.1', 4334)
netHandle:setPrefix('TEST')
netHandle:setHook(onReceivePackets)
endЧтобы оборвать соединение с сервером недостаточно удалить переменную netHandle это лишь приведет к вылету скрипта с ошибкой, ибо функция SLNetLoop, которая используется в бесконечном цикле, попытается выполнить неизвестную инструкцию из переменной, которая была удалена - не делайте так!
Для безопасного отключения необходимо просто пересоздать переменную повторно использовав SLNetInit, но уже не устанавливая новые параметры в виде набора IP-адреса и порта. Также вы можете удалить переменную в том случае, если в функции SLNetLoop и других стоит проверка на её существование.
Функцию SLNetLoop необходимо использовать в бесконечном цикле скрипта, эта функция отвечает за последовательный вызов функций отправки и получения пакетов. Без этой функции пакеты не будут отправляться и приниматься, соответственно модуль не сможет быть использован в полной мере.
while true do
netHandle:loop()
endЧтобы отправлять пакеты используется функция SLNetSend, всего у этой функции две перегрузки - для сервера и для клиента. Различие лишь в наличие двух предпоследних аргументов - IP-адрес и порт получателя. В клиенте же передаются только эти аргументы: PacketID, bitStream, Priority. Соответственно серверная версия этой функции будет получать дополнительные аргументы, в общем выглядит так: PacketID, bitStream, Address, Port, Priority.
Подробнее о том, как использовать SL:NET BitStream можно почитать на другой странице SL:NET Wiki.