Advertisement
Snusmumriken

bandage.lua tcp-async client-server lib

Nov 30th, 2018
256
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.71 KB | None | 0 0
  1. local socket     = require('socket')
  2. local tcp_master = socket.tcp4 or socket.tcp
  3.  
  4. --[[
  5.     структура сообщения:
  6.     [служебный символ][длина сообщения]\r\n
  7.     [тело сообщения]
  8.  
  9.     (почти аналогично redis)
  10.  
  11.     Служебные символы (типы сообщений):
  12.     @ - коннект
  13.     # - дисконнект
  14.     $ - обычное сообщение
  15.     % - пинг
  16. ]]
  17.  
  18.  
  19. local client = {}
  20. client.__index = client
  21.  
  22. -- создание самостоятельного клиента
  23. function client:new(host, port)
  24.     local sock = tcp_master()
  25.     assert(type(host) == 'string', 'arg#1: string host expected, got ' .. type(host))
  26.     assert(type(port) == 'number', 'arg#2: number port expected, got ' .. type(host))
  27.    
  28.     local status, error = sock:connect(host, port)
  29.    
  30.     if not status then
  31.         return nil, error
  32.     end
  33.    
  34.     local self = self:_new(sock)
  35.    
  36.     self:onAccept()
  37.    
  38.     return self
  39. end
  40.  
  41. -- заполнение полей отдельно
  42. function client:_new(sock, server)
  43.     local host, port = sock:getpeername()
  44.     local self = setmetatable({}, self)
  45.  
  46.     self:__debug('is created' .. (server and ' by ' .. server or ''))
  47.  
  48.     self.sock = sock
  49.     self.host = host
  50.     self.port = port
  51.    
  52.     self.server  = server -- связь с родительским классом, если создаёт сервер
  53.    
  54.     self.time    = socket.gettime()
  55.     self.dt      = self.time
  56.     self.timeout = 60 -- ping
  57.     self.timer   = 0
  58.     self.delay   = 2
  59.    
  60.     self.msgtype = nil -- для состояния 'приём сообщения',
  61.     self.length  = nil -- тип и длина принимаемого
  62.     self.message = ""  -- контейнер для частей сообщения
  63.    
  64.     self.dbg = nil -- отладочный вывод
  65.    
  66.     -- мы не будем ждать пока он нам что-то присылает
  67.     -- потому что в это время нам кто-то другой может что-то слать
  68.     self.sock:settimeout(0)
  69.     return self
  70. end
  71.  
  72. function client:ping()
  73.     local time = socket.gettime()
  74.     local dt   = time - self.dt
  75.    
  76.     self.dt    = time
  77.     self.timer = self.timer - dt
  78.    
  79.     if self.timer < 0 then
  80.         self.timer = self.delay
  81.         self:send('ping', '%')
  82.     end
  83.    
  84.     if time - self.time > self.timeout then
  85.         self:disconnect('disconnect by timeout')
  86.     end
  87. end
  88.  
  89. -- отправка сообщения соединения (передача инфы всякой, например)
  90. function client:connect(message)
  91.     message = message or "HELLO"
  92.     self:send(message, '@')
  93. end
  94.  
  95. function client:disconnect(message)
  96.     message = message or 'disconnect'
  97.    
  98.     if self.server then
  99.         local server = self.server
  100.         self.server = nil
  101.         return server:removeClient(self, message)
  102.     end
  103.    
  104.     self:send(message, '#')
  105.     self:onClosed()
  106.    
  107.     self.sock:close()
  108.     self.sock = nil
  109. end
  110.  
  111. function client:onAccept(msg)
  112.     self:__debug('call onAccept')
  113. end
  114.  
  115. function client:onClosed(msg)
  116.     self:__debug('call onClosed')
  117. end
  118.  
  119. function client:onConnect(msg)
  120.     self:__debug('call onConnect', msg:sub(1, 10))
  121. end
  122.  
  123. function client:onMessage(msg)
  124.     self:__debug('call onMessage', msg:sub(1, 10))
  125. end
  126.  
  127. function client:onDisconnect(msg)
  128.     self:__debug('call onDisconnect', msg:sub(1, 10))
  129. end
  130.  
  131. function client:onPing(message)
  132.     if message == 'ping' then
  133.         self:send('pong', '%')
  134.     end
  135.     if message == 'pong' then
  136.         self.time = socket.gettime()
  137.     end
  138. end
  139.  
  140. function client:send(message, msgtype)
  141.     if not self.sock then
  142.         return nil, 'Connection close'
  143.     end
  144.     message = message or ''
  145.     msgtype = msgtype or '$' -- обычное сообщение по умолчанию
  146.  
  147.     -- [тип][длина сообщения]\r\n[тело сообщения]
  148.     self.sock:send(msgtype .. #message .. '\r\n' .. message)
  149. end
  150.  
  151. -- Приём проводится в два этапа:
  152. -- приём заголовка (данных о сообщении), и приём самого тела сообщения.
  153. function client:receive()
  154.     if not self.sock then
  155.         return nil, 'Connection closed'
  156.     end
  157.  
  158.     -- Принимаем заголовок, если есть
  159.     local _, status = self:receiveHeader()
  160.        
  161.         -- Принимаем тело, если есть
  162.     local message, msgtype = self:receiveBody()
  163.  
  164.     if msgtype == '$' then
  165.         msgtype = 'onMessage'
  166.     end
  167.    
  168.     if msgtype == '@' then
  169.         msgtype = 'onConnect'
  170.     end
  171.    
  172.     if msgtype == '#' then
  173.         msgtype = 'onDisconnect'
  174.     end
  175.    
  176.     if msgtype == '%' then
  177.         self:onPing(message)
  178.         return
  179.     end
  180.    
  181.     if self[msgtype] then
  182.         self[msgtype](self, message)
  183.     end
  184.    
  185.     -- если нас создал сервер и у сервера присутствует такой колбек
  186.     if self.server and self.server[msgtype] then
  187.         self.server[msgtype](self.server, message, self)
  188.     end
  189.    
  190.     if self:ping() then
  191.         msgtype = 'closed'
  192.     end
  193.    
  194.     if msgtype == 'closed' then
  195.         self:disconnect('timeout disconnect')
  196.         return nil, msgtype
  197.     end
  198.    
  199.     if status == 'closed' then
  200.         return self:disconnect()
  201.     end
  202.    
  203.     return message, msgtype
  204. end
  205.  
  206. function client:receiveHeader()
  207.  
  208.     -- у нас уже может быть принятый заголовок
  209.     if self.msglen then return end
  210.        
  211.     -- 1. Начало приёма сообщения
  212.     -- принимаем 'строку' с \r\n
  213.     local message, status = self.sock:receive('*l')
  214.    
  215.     -- все неправильные вещи - игнорируем
  216.     if not message then return message, status end
  217.    
  218.     -- вырезаем первый символ и длину
  219.     local msgtype, msglen = message:sub(1, 1), tonumber(message:sub(2))
  220.  
  221.     if not msglen then return nil, status end
  222.    
  223.     self.msglen  = msglen
  224.     self.msgtype = msgtype
  225.     self.message = ""
  226.     return nil, status
  227. end
  228.  
  229. function client:receiveBody()
  230.     -- у нас может быть непринятый заголовок
  231.     if not self.msglen then return end
  232.    
  233.     -- 2. Когда у нас уже есть длина сообщения:
  234.     -- Мы должны асинхронно принять тело сообщения,
  235.     -- в случае если на текущий момент идёт приём
  236.     -- (мы приняли первую часть с типом и длиной)
  237.     -- то теперь мы просто ждём пока сообщение не дойдёт окончательно.
  238.     -- На малых данных, оба этих этапа выполняются подряд, в одно действие.
  239.  
  240.     local i = 1 -- чтобы сильно не забивали канал огромными сообщениями
  241.    
  242.     -- "стандатный" размер буфера приёма сообщений - 8192
  243.     local bufsize = self:append("", 8192)
  244.    
  245.    
  246.     local message, status, partial = self.sock:receive(bufsize)
  247.    
  248.     while message or partial and bufsize > 0 do
  249.         i = i + 1
  250.         message = message or partial
  251.         bufsize = self:append(message or partial, bufsize)
  252.        
  253.         -- Прерываем на больших данных или если уже всё приняли
  254.         if bufsize == 0 or i > 5 then break end
  255.        
  256.         message, status, partial = self.sock:receive(bufsize)
  257.     end
  258.    
  259.     if bufsize == 0 then
  260.         local message = self.message
  261.         local msgtype = self.msgtype
  262.        
  263.         self.message = ""
  264.         self.msgtype = nil
  265.         self.msglen  = nil
  266.         return message, msgtype
  267.     end
  268. end
  269.  
  270. -- Функция, которая цепляет кусок длинного сообщения,
  271. -- и возвращает размер следующего блока для приёма
  272.  
  273. function client:append(chunk, bufsize)
  274.     self.message = self.message .. chunk
  275.     self.msglen = self.msglen - #chunk
  276.     if self.msglen < bufsize then
  277.         return self.msglen
  278.     end
  279.     return bufsize
  280. end
  281.  
  282. function client:__tostring()
  283.     if self.sock then
  284.         local shost, sport = self.sock:getsockname()
  285.         local phost, pport = self.sock:getpeername()
  286.         return 'Client object [' .. shost .. ':' .. sport .. ']>[' .. phost .. ':' .. pport .. ']'
  287.     end
  288.     return 'Client object [unconnected]'
  289. end
  290.  
  291. function client:__concat(other)
  292.     return tostring(self) .. tostring(other)
  293. end
  294.  
  295. function client:__debug(...)
  296.     if self.dbg or self.server and self.server.dbg then
  297.         print('debug ' .. self .. ' ' .. tostring(...), select(2, ...))
  298.     end
  299. end
  300.  
  301. setmetatable(client, {__call = client.new})
  302.  
  303.  
  304. local server = {}
  305. server.__index = server
  306.  
  307.  
  308. function server:new(port)
  309.     self = setmetatable({}, self)
  310.     self.port = port or 7777
  311.     self.sock = tcp_master()
  312.     self.clients = {}
  313.    
  314.     local succ, res = self.sock:bind('*', self.port)
  315.     if not succ then
  316.         error('server:new(' .. port .. ') error: ' .. tostring(res), 2)
  317.     end
  318.     self.sock:listen()
  319.     self.sock:settimeout(0)
  320.     self.sock:setoption('keepalive',   true)
  321.     self.sock:setoption('tcp-nodelay', true)
  322.    
  323.     self.dbg = nil
  324.    
  325.     return self
  326. end
  327.  
  328. function server:onAccept(client)
  329.     self:__debug('call onAccept ' .. client)
  330. end
  331.  
  332. function server:onPing(message, client)
  333.     client:onPing(message)
  334. end
  335.  
  336. function server:onClosed(client)
  337.     self:__debug('call onClosed ' .. client)
  338. end
  339.  
  340. function server:onConnect(msg, client)
  341.     self:__debug('call onConnect ' .. client, msg:sub(1, 10))
  342. end
  343.  
  344. function server:onMessage(msg, client)
  345.     self:__debug('call onMessage ' .. client, msg:sub(1, 10))
  346. end
  347.  
  348. function server:onDisconnect(msg, client)
  349.     self:__debug('call onDisconnect ' .. client, msg:sub(1, 10))
  350. end
  351.  
  352. function server:newClient(sock)
  353.     -- указываем заодно ссылку на себя
  354.     local client  = client:_new(sock, self)
  355.    
  356.     self.clients[client.sock] = client
  357.     return client
  358. end
  359.  
  360. function server:removeClient(client, message)
  361.     local sock = client.sock
  362.    
  363.     self:__debug('remove ' .. client .. ' start')
  364.     local client = self.clients[sock]
  365.     if not client then return end
  366.    
  367.     self:onClosed(client)
  368.     client:disconnect(message)
  369.    
  370.     self.clients[sock] = nil
  371. end
  372.  
  373. function server:getClientList()
  374.     local l = {}
  375.     for k, v in pairs(self.clients) do
  376.         table.insert(l, v)
  377.     end
  378.     return l
  379. end
  380.  
  381. function server:accept()
  382.     local sock = self.sock:accept()
  383.     while sock do
  384.         socket.sleep(2)
  385.         local client = self:newClient(sock)
  386.         self:onAccept(client)
  387.         sock = self.sock:accept()
  388.     end
  389. end
  390.  
  391. function server:update()
  392.     self:accept()
  393.     for sock, client in pairs(self.clients) do
  394.         local message, status = client:receive()
  395.         while message do
  396.             -- Повторяем, пока есть что принять от данного клиента
  397.             message, status = client:receive()
  398.         end
  399.     end
  400. end
  401.  
  402. function server:send(client, message, type)
  403.     client:send(message, type)
  404. end
  405.  
  406. function server:shutdown(message)
  407.     for sock, client in pairs(self.clients) do
  408.         client:disconnect('server shutdown')
  409.     end
  410.     self.sock:close()
  411. end
  412.  
  413. function server:__tostring()
  414.     local shost, sport = self.sock:getsockname()
  415.    
  416.     return 'Server object [' .. shost .. ':' .. sport .. ']'
  417. end
  418.  
  419. function server:__concat(other)
  420.     return tostring(self) .. tostring(other)
  421. end
  422.  
  423. function server:__debug(...)
  424.     if self.dbg then
  425.         print('debug ' .. self .. ' ' .. tostring(...), select(2, ...))
  426.     end
  427. end
  428.  
  429. setmetatable(server, {__call = server.new})
  430.  
  431. if ... then -- если цепляется как библиотека - возвращаем библиотеку
  432.     return {
  433.         server = server,
  434.         client = client
  435.     }
  436. end
  437. --[[ Схема работы:
  438.     И клиенты и серверы могут иметь набор колбеков:
  439.     onAccept     - момент стыковки,
  440.     onConnect    - когда другая сторона прислала connect-сообщение [obj]:send(message, "@")
  441.     onMessage    - другая сторона прислала message-сообщение [obj]:send(message)
  442.     onDisconnect - другая сторона прислала disconnect-сообщение [obj]:send(message, "#")
  443.     onClosed     - другая сторона закрыла соединение
  444.  
  445.    
  446.     В случае сервера, если повесить колбеки на подсоединившихся клиентов,
  447.     будут вызываться сразу оба колбека: соответствующий клиентский и соответствующий серверный.
  448.    
  449.     Уточнение: если одна из сторон вызвала client:disconnect(), то другая сторона вызовет только onClosed.
  450. ]]
  451.  
  452. -- Примеры
  453.  
  454. local srv = server:new(7777)
  455.  
  456. function srv:onAccept(client)
  457.     print('Client connected! ' .. client)
  458.     client:send("Hai, what's up?")
  459. end
  460.  
  461. function srv:onConnect(message, client)
  462.     print('Client sends us connect message! ' .. client, message)
  463. end
  464.  
  465. function srv:onMessage(message, client)
  466.     print('Client sends us message! ' .. client, message)
  467.     -- второй аргумент - заставляет другую сторону вызвать колбек:
  468.     -- @ -- onConnect, # - onDisconnect, $ - onMessage
  469.     -- по умолчанию - onMessage
  470.     -- Сервер может вызвать client:disconnect(),
  471.     -- но тогда другая сторона сразу обрубится, с вызовом onClosed.
  472.     client:send("Nope, get out here!", "#")
  473. end
  474.  
  475. function srv:onDisconnect(message, client)
  476.     print('Client disconnect from us! ' .. client, message)
  477. end
  478.  
  479. function srv:onClosed(client)
  480.     print(self .. ' client closes connection ' .. client)
  481. end
  482.  
  483.  
  484. local cli = client:new('localhost', 7777)
  485.  
  486. function cli:onMessage(message)
  487.     print("Server sends us message: " .. message)
  488.     self:send("Do you want to keep me?")
  489. end
  490.  
  491. function cli:onDisconnect(message)
  492.     print("Server disconnect us with message: " .. message)
  493.     self:disconnect('(((')
  494. end
  495.  
  496. function cli:onClosed()
  497.     print(self .. ' server closes connection')
  498. end
  499.  
  500.  
  501. cli:connect("Hello! i'm novice!")
  502.  
  503. while true do
  504.     srv:update()
  505.     local a, b = cli:receive()
  506.     socket.sleep(.1)
  507. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement