Инструменты пользователя

Инструменты сайта


lm3:ce:t_chat

Туториал: многопользовательский режим (чат-комната)

Концепция логических машин платформы Лямбда-Мю 3 позволяет с легкостью решать задачи работы с многими пользователями:

  • авторизация
  • выделение ресурсов
  • обработка запросов
  • статистика
  • пр.

Разберем взаимодействие платформы с несколькими клиентами.

Пример реализации

Чат-комната. Пользователи находятся в одной виртуальной комнате и обмениваются текстовыми сообщениями.

Репозитарий

Файловая структура проекта представлена ниже.

Необходимые скрипты туториала находятся в репозитарии lm3.examples/chat.

Чтобы запустить платформу скачайте бинарный файл и необходимые библиотеки из репозитария lm3.engine.ce и скопируйте в корень проекта.

Исходные данные

Сторону клиента реализует Android-приложение с

  • запрограммированным модулем связи с платформой
  • протоколом связи, который рассмотрен ниже

Цель проекта

Разработка взаимодействия платформы Лямбда-Мю 3 с многими клиентами на конкретном примере.

Протокол запросов и ответов

Запрос:

{ Command = "DoAuthByName", Data = <string> }

Ответ (всем пользователям):

{ Command = "OnSendAll", Data = "Member connected: "..<string> }

Запрос:

{ Command = "DoSendAll", Data = <string> }

Ответ (всем пользователям):

{ Command = "OnSendAll", Data = "Member: "..<string> }

Файловая структура проекта

Взаимосвязи

Объекты основной логической машины:

  • TCP-server server: прием и передача данных клиенту
  • DataPack packer: распаковка и запаковка данных клиенту
  • Auth: авторизация клиента; вызов создания ЛМ; связь клиента с соответствующей ЛМ
  • Сlients (наследник LMList): управление ЛМ

Диаграмма связей:

На логических машинах запросы обрабатываются следующим образом:

  • команда авторизации DoAuthByName присваивает переменной userName значение и вызывает OnSendAll
  • команда сообщения DoSend вызывает OnSendAll, если пользователь авторизирован

Скрипты

main / start.lua

lm3:Log("Starting server")
 
lm3:LoadLibrary( {Alias = "sys", FileName = "lm3system"} )
lm3:Include("main_lib.lua")
 
lm3:CreateLObject( { LibAlias = "sys", Type = "TCPServer", Name = "server" } )
server:SetAttr("SocCount", lmconf.MaxClientCount)
 
lm3:CreateLObject( { LibAlias = "sys", Type = "DataPack", Name = "packer" } )
packer:SetAttr("BufCount", lmconf.MaxClientCount)
 
lm3:CreateObject( { Type = "LMList", Name = "clients" } )
 
init_Auth("Auth")
 
lm3:AddHandler("OnStart", function(o, ev)
    -- проверка конфигурации ЛМ
    if clients:DoPrepare(lmconf.BasePath.."client_lm/config.lua") == false then
        lm3:Log("client_lm prepare failed")
    end
end)
 
Auth:AddHandler("OnReply", function(o, ev)
  lm3:Log("Auth: OnReply: tag: "..tostring(ev.Tag).." : "..lm3:DataToString(ev.Data))
end)
 
-- связи объектов
server:Connect("OnConnect", "Auth", "DoConnect")
server:Connect("OnDisconnect", "Auth", "DoDisconnect")
server:Connect("OnIncoming", "packer", "DoUnpack")
packer:Connect("OnPack", "server", "DoSend")
 
packer:Connect("OnUnpack", "Auth", "DoIncoming")
Auth:Connect("OnReply", "packer", "DoPackTagged")
 
Auth:Connect("OnStartClientLM", "clients", "DoStart")
Auth:Connect("OnStopClientLM", "clients", "DoStop")
Auth:Connect("OnClientEvent", "clients", "DoEvent")
clients:Connect("OnEvent", "Auth", "DoClientEvent")

main / main_lib.lua

-- объект Auth
--
function init_Auth(_name)
    local obj = lm3:CreateObject( { Type = "Base", Name = _name } )
 
    obj.ClientList = {}
    for i = 1, lmconf.MaxClientCount do
        obj.ClientList[i] = {
            isConnected = false,
            LMName = "",
        }
    end
 
    function obj.TagFromLMName(_name)
        return tonumber(_name:sub(5))
    end
 
    -- подключение нового клиента
    -- старт ЛМ
    obj:AddInEvent("DoConnect", "int", function(o, ev)
        lm3:Log("New client connected: "..tostring(ev))
        obj.ClientList[ev].isConnected = true
        obj.ClientList[ev].LMName = "clm_"..tostring(ev)
        lm3:Log("Start client LM:")
        if obj:OnStartClientLM( { Name = obj.ClientList[ev].LMName } ) == true then
            lm3:Log("Start client LM complete.")
        else
            lm3:Log("ERROR: Start client LM failed.")
            obj.ClientList[ev].isConnected = false
        end
    end)
 
    -- отключение клиента
    -- остановка ЛМ
    obj:AddInEvent("DoDisconnect", "int", function(o,ev)
        lm3:Log("Client disconnected: "..tostring(ev))
        obj:OnStopClientLM(obj.ClientList[ev].LMName)
        obj.ClientList[ev].isConnected = false
    end)
 
    -- распределение данных от клиента
    obj:AddInEvent("DoIncoming", "table[Tag:int,Data:table[Command:string,Data:string]]", function(o, ev)
        if obj.ClientList[ev.Tag].isConnected ~= true then
            lm3:Log("ERROR: Incoming data from not connected client: "..tostring(ev.Tag))
        else
            lm3:Log("Incoming data from client with tag: "..tostring(ev.Tag).." : "..lm3:DataToString(ev.Data))
            obj:OnClientEvent({ LMName = obj.ClientList[ev.Tag].LMName, EventName = "OnExtEvent", Data = ev.Data })
        end
    end)
 
    -- обработка данных от ЛМ
    obj:AddInEvent("DoClientEvent", "table[LMName:string,EventName:string,Data:table[Command:string,Data:string]]", function(o, ev)
        if ev.Data.Command == "OnSendAll" then
            for i = 1, lmconf.MaxClientCount do
                if obj.ClientList[i].isConnected then
                    obj:OnReply( { Tag = i, Data = ev.Data } )
                end
            end
        else
            local iClient = obj.TagFromLMName(ev.LMName)
            obj:OnReply( { Tag = iClient, Data = ev.Data } )
        end
    end)
 
    -- отправить данные клиенту
    obj:AddOutEvent("OnReply", "table[Tag:int,Data:table[Command:string,Data:string]]")
 
    -- отправить данные в ЛМ
    obj:AddOutProc("OnClientEvent", "table[LMName:string,EventName:string,Data:any]", "bool")
 
    obj:AddOutProc("OnStartClientLM", "table[Name:string]", "bool")
 
    obj:AddOutProc("OnStopClientLM", "string", "bool")
 
    return obj
end

main / client_lm / start.lua

lm3:LoadLibrary( {Alias = "sys", FileName = "lm3system"} )
 
-- объект Timer для alive сообщение
lm3:CreateObject( { Type = "Timer", Name = "timer" } )
timer:SetAttr("Interval", 30*1000)
 
lm3:AddHandler("OnStart", function(o, ev)
    timer:DoEnable(true)
end)
 
userName = nil
 
-- обработчик ивента с основной логической машины
lm3:AddHandler("OnExtEvent", function(o, ev)
    if ev.Command == "DoAuthByName" then
        userName = ev.Data
        lm3:DoExtEvent( { Command = "OnSendAll", Data = "Member connected: "..userName } )
    elseif ev.Command == "DoSendAll" then
        if userName == nil then
            lm3:Log("Error: userName == nil")
        else
            lm3:DoExtEvent( { Command = "OnSendAll", Data = "Member "..userName..": "..ev.Data } )
        end
    end
end)
 
timer:AddHandler("OnTick", function(o, ev)
    --lm3:Log("OnTick: send Alive")
    lm3:DoExtEvent( { Command = "Alive", Data = "" } )
end)

Логи

[client 1]

-- получение уникального имени
2020-11-27 19:02:14.044 1940-1940/com.example.lm3_client D/lm3: application: onCreate: uniqueName: 3360d
2020-11-27 19:02:14.060 1940-1984/com.example.lm3_client D/lm3: application: setNetworkStatus: 2
-- подключение к серверу
2020-11-27 19:02:14.060 1940-1984/com.example.lm3_client D/lm3: connect: startServerConnection
2020-11-27 19:02:14.061 1940-1985/com.example.lm3_client D/lm3: connect: start
2020-11-27 19:02:14.084 1940-1940/com.example.lm3_client D/lm3: activity: main: OnCreate
2020-11-27 19:02:14.188 1940-1985/com.example.lm3_client D/lm3: connect: setServerConnection: true
2020-11-27 19:02:14.189 1940-1985/com.example.lm3_client D/lm3: connect: done
2020-11-27 19:02:14.189 1940-1985/com.example.lm3_client D/lm3: connect: end
2020-11-27 19:02:14.189 1940-2027/com.example.lm3_client D/lm3: connect: start listen
2020-11-27 19:02:14.234 1940-1940/com.example.lm3_client D/lm3: activity: main: onResume
-- авторизация
2020-11-27 19:02:15.189 1940-2027/com.example.lm3_client D/lm3: listen: DoAuthByName: 3360d
2020-11-27 19:02:15.190 1940-2027/com.example.lm3_client D/lm3: listen: serverSend
2020-11-27 19:02:15.252 1940-2027/com.example.lm3_client D/lm3: listen: OnSendAll: Member connected: 3360d
2020-11-27 19:02:24.392 1940-1940/com.example.lm3_client D/lm3: activity: main: 
-- отправка сообщения
button OnClick: message: Hello
2020-11-27 19:02:24.392 1940-1940/com.example.lm3_client D/lm3: listen: DoSendAll: Hello
2020-11-27 19:02:24.392 1940-1940/com.example.lm3_client D/lm3: listen: serverSend
2020-11-27 19:02:24.424 1940-2027/com.example.lm3_client D/lm3: listen: OnSendAll: Member 3360d: Hello
2020-11-27 19:02:34.153 1940-2027/com.example.lm3_client D/lm3: listen: OnSendAll: Member 65c8c: Hi

[client 2]

-- получение уникального имени
2020-11-27 19:01:55.396 14523-14523/com.example.lm3_client D/lm3: application: onCreate: uniqueName: 65c8c
2020-11-27 19:01:55.527 14523-14556/com.example.lm3_client D/lm3: application: setNetworkStatus: 2
-- подключение к серверу
2020-11-27 19:01:55.527 14523-14556/com.example.lm3_client D/lm3: connect: startServerConnection
2020-11-27 19:01:55.529 14523-14557/com.example.lm3_client D/lm3: connect: start
2020-11-27 19:01:55.544 14523-14557/com.example.lm3_client D/lm3: connect: setServerConnection: true
2020-11-27 19:01:55.545 14523-14557/com.example.lm3_client D/lm3: connect: done
2020-11-27 19:01:55.545 14523-14557/com.example.lm3_client D/lm3: connect: end
2020-11-27 19:01:55.545 14523-14559/com.example.lm3_client D/lm3: connect: start listen
2020-11-27 19:01:55.573 14523-14523/com.example.lm3_client D/lm3: activity: main: OnCreate
2020-11-27 19:01:55.716 14523-14523/com.example.lm3_client D/lm3: activity: main: onResume
-- авторизация
2020-11-27 19:01:56.546 14523-14559/com.example.lm3_client D/lm3: listen: DoAuthByName: 65c8c
2020-11-27 19:01:56.546 14523-14559/com.example.lm3_client D/lm3: listen: serverSend
2020-11-27 19:01:56.580 14523-14559/com.example.lm3_client D/lm3: listen: OnSendAll: Member connected: 65c8c
2020-11-27 19:02:14.398 14523-14559/com.example.lm3_client D/lm3: listen: OnSendAll: Member connected: 3360d
2020-11-27 19:02:23.602 14523-14559/com.example.lm3_client D/lm3: listen: OnSendAll: Member 3360d: Hello
-- отправка сообщения
2020-11-27 19:02:33.103 14523-14523/com.example.lm3_client D/lm3: activity: main: button OnClick: message: Hi
2020-11-27 19:02:33.103 14523-14523/com.example.lm3_client D/lm3: listen: DoSendAll: Hi
2020-11-27 19:02:33.103 14523-14523/com.example.lm3_client D/lm3: listen: serverSend
2020-11-27 19:02:33.139 14523-14559/com.example.lm3_client D/lm3: listen: OnSendAll: Member 65c8c: Hi

[server] LM_main.log

[2020-11-27 19:01:30][INFO:LMInit][Start logic machine v3.3(Build:555) ----------------------------------]
[2020-11-27 19:01:30][LUA:LMCore][Starting server]
[2020-11-27 19:01:30][TRACE:LMInit][Create library object: server]
[2020-11-27 19:01:30][TRACE:LMInit][Create library object: packer]
[2020-11-27 19:01:30][TRACE:LMInit][Create object: clients]
[2020-11-27 19:01:30][TRACE:LMInit][Create object: Auth]
[2020-11-27 19:01:30][INFO:LMInit][Initialization complete.]
[2020-11-27 19:01:48][LUA:LMCore][New client connected: 1]
[2020-11-27 19:01:48][LUA:LMCore][Start client LM:]
[2020-11-27 19:01:48][LUA:LMCore][Start client LM complete.]
-- авторизация первого клиента
[2020-11-27 19:01:49][LUA:LMCore][Incoming data from client with tag: 1 : [Table:2]
  "Command" = [String] "DoAuthByName"
  "Data" = [String] "65c8c"]
[2020-11-27 19:01:49][LUA:LMCore][Auth: OnReply: tag: 1 : [Table:2]
  "Command" = [String] "OnSendAll"
  "Data" = [String] "Member connected: 65c8c"]
[2020-11-27 19:02:06][LUA:LMCore][New client connected: 2]
[2020-11-27 19:02:06][LUA:LMCore][Start client LM:]
[2020-11-27 19:02:06][LUA:LMCore][Start client LM complete.]
-- авторизация второго клиента
[2020-11-27 19:02:07][LUA:LMCore][Incoming data from client with tag: 2 : [Table:2]
  "Command" = [String] "DoAuthByName"
  "Data" = [String] "3360d"]
[2020-11-27 19:02:07][LUA:LMCore][Auth: OnReply: tag: 1 : [Table:2]
  "Command" = [String] "OnSendAll"
  "Data" = [String] "Member connected: 3360d"]
[2020-11-27 19:02:07][LUA:LMCore][Auth: OnReply: tag: 2 : [Table:2]
  "Command" = [String] "OnSendAll"
  "Data" = [String] "Member connected: 3360d"]
-- сообщение от первого клиента
[2020-11-27 19:02:16][LUA:LMCore][Incoming data from client with tag: 2 : [Table:2]
  "Command" = [String] "DoSendAll"
  "Data" = [String] "Hello"]
[2020-11-27 19:02:16][LUA:LMCore][Auth: OnReply: tag: 1 : [Table:2]
  "Command" = [String] "OnSendAll"
  "Data" = [String] "Member 3360d: Hello"]
[2020-11-27 19:02:16][LUA:LMCore][Auth: OnReply: tag: 2 : [Table:2]
  "Command" = [String] "OnSendAll"
  "Data" = [String] "Member 3360d: Hello"]
[2020-11-27 19:02:18][LUA:LMCore][Auth: OnReply: tag: 1 : [Table:2]
  "Command" = [String] "Alive"
  "Data" = [String] ""]
-- сообщение от второго клиента
[2020-11-27 19:02:26][LUA:LMCore][Incoming data from client with tag: 1 : [Table:2]
  "Command" = [String] "DoSendAll"
  "Data" = [String] "Hi"]
[2020-11-27 19:02:26][LUA:LMCore][Auth: OnReply: tag: 1 : [Table:2]
  "Command" = [String] "OnSendAll"
  "Data" = [String] "Member 65c8c: Hi"]
[2020-11-27 19:02:26][LUA:LMCore][Auth: OnReply: tag: 2 : [Table:2]
  "Command" = [String] "OnSendAll"
  "Data" = [String] "Member 65c8c: Hi"]
[2020-11-27 19:02:36][LUA:LMCore][Auth: OnReply: tag: 2 : [Table:2]
  "Command" = [String] "Alive"
  "Data" = [String] ""]
[2020-11-27 19:02:48][LUA:LMCore][Auth: OnReply: tag: 1 : [Table:2]
  "Command" = [String] "Alive"
  "Data" = [String] ""]
[2020-11-27 19:03:06][INFO:LMCore][Stop logic machine.]
lm3/ce/t_chat.txt · Последнее изменение: 2020/11/30 15:06 — ruben