[RGame Engine] Сетевой протокол движка

[RGame Engine] Сетевой протокол движка

roxblnfk
Копия моей статьи с ныне мёртвого develnet.ru


В начало


Здесь под сетевым протоколом понимается не TCP, а класс, входящий в состав сетевого кода движка.

Рассматриваемые классы rxnetcl и rxnetsv являются расширениями классов rxnetcllw и rxnetsvlw (классы, упрощающие и реализующие работу с сокетами на "низком" уровне) до сетевого протокола, которым пользуются клиент и сервер.


Главная особенность заключается в том, что всё это работает на неблокирующих сокетах. Это можно назвать достоинством, вызывающим множество проблем и сложностей :)

  • Расшифровывает "сырые" (бинарные) данные.
  • Шифрует данные для передачи (собирает пакеты данных[*]).
  • Следит за наличием соединения между клиентом и сервером. Пока факт разъединения пользователя и сервера определяется двумя способами:
  • автоматическое пингование при "простое" соединения. Если одна из сторон не присылает данные через определённое время (опция "TimeLimit"), то соединение считается разорванным. Значение опции "PingMaxTime" должно быть меньше в 2-3 раза значения опции "TimeLimit" для нормальной работы пинга (при почти равных значениях пингование будет производиться, но оно будет бессмысленным; при большем же значении функционал этот вообще работать не будет — соединение разорвётся до того, как настанет время начала пинга).
  • ошибка при попытке отправить данные второй стороне как бы намекает, что соединение также разорвано (в этом месте кода пока есть "преведы": rxnetcllw/rxnetsvlw функция lw_send).


При обнаружении разъединения вызывается функция disconnect(), передающая информацию об этом событии движку на функцию onDisconnect().


Недостатки:

  • При пинговании (да и вообще) задержка между пакетами не считается. (исправимо)
  • Нет шифрования данных. (исправимо)
  • Нет сжатия не бинарных данных. (исправимо, функции pack(), unpack())


[*] Заметки подраздела:

  • Пакеты данных
    Каждый пакет состоит из двух основных частей: команды (заголовка) и контента.
    Контент контент содержит информацию любого из следующих типов: array/integer/float(real)/string/boolean/object/null (новые типы данных добавляйте в функции pack(), unpack()).
    Команда
    - целочисленное значение, позволяющее определить предназначение контента.

Каждый пакет начинается с одних и тех же двух байт, которые заданы в переменной FB. Значения переменной FB можно менять, но они должны совпадать на клиенте и сервере!


Нужно расписывать переменные класса и функции? Думаю по коду и так понятно :Р


Ответы на вопросы

Acid-UA
Интересует вопрос, почему команда должна быть численным значением? Ведь команда в виде слова гораздо понятнее, в числах можно и запутаться?

При проектировании протокола я ставил упор на минимизацию скорости обработки и минимизацию трафика


Чтобы понять, почему число, давайте посмотрим на структуру протокола по байтам.

Вот комментарий к функции pack() рассматриваемого класса:

// | 2B head | 4B command | 4B type | 4B content length / value | [0-2GB content :)] | <= 14 байт минимум


  1. первые 2 байта - берутся из переменной $FB, знаменуют начало нового пакета. Если они не совпадают, то, значит, произошла рассинхронизация, либо используется чужеродный протокол — повод разорвать соединение.
  2. далее идут фиксировано 4 байта - команда
  3. потом 4 байта (можно было бы и 2 байта взять или даже 1) - способ упаковки контента
  4. 4 байта, указывающие на длину контента, который идёт следом. Иногда эти 4 байта и есть контент (к примеру bool или int)
  5. 0-2GiB - контент.


У нас используются 32-х битные числа, поэтому они как-раз умещаются в 4 байта (1 байт = 8 бит, 4*8=32 бита) функцией php pack().


Если команда будет строкой, то эти 4 байта пункта 2. выглядели бы как:

2.1 сначала 1-4 байта, указывающие на длину строковой команды

2.2 не сжатая строка - команда


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


Что касается удобства восприятия - оставьте комментарий напротив числа, или можете использовать константы для этого: define('cc_USER_DEAD', 123);

Вы эту числовую команду должны использовать минимум раз: при обработке принятого пакета и при отправке. Если номер команды встречается где-то ещё, то, возможно, вы организовали код не правильно!


Также ознакомьтесь с системой отправки событий в теме Расширения движка, обработчики команд и событий — эвенты отправляются со строковым описанием…

Report Page