Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- require'relativerequire'
- math.randomseed(os.time())
- local random = math.random
- local serpent = require'serpent'
- local parser = require'parser'
- local socket = require'socket'
- local udp = socket.udp4 or socket.udp
- local hash = parser.hash
- local dbg = false
- function printf(s, ...)
- if not dbg then return end
- print(s:format(...))
- end
- local function tableprinter(t, opts, pool, lvl)
- pool = pool or {}
- opts = opts or {}
- local res = {}
- lvl = lvl or 1
- for k, v in pairs(t) do
- if not opts[k] then
- if type(v) == 'table' and not pool[v] then
- if not pool[v] then
- pool[v] = 1
- table.insert(res, k..'::\n'..(' '):rep(lvl+1)..tableprinter(v, opts, pool, lvl + 1))
- end
- else
- table.insert(res, k..': '..tostring(v))
- end
- end
- end
- return table.concat(res, '\n'..(' '):rep(lvl))
- end
- local function b(char)
- local char = char:byte()
- local res, rest = {}
- while char > 0 do
- char = char * .5
- if math.floor(char) == char then
- table.insert(res, 1, 0)
- else
- char = char - .5
- table.insert(res, 1, 1)
- end
- end
- table.insert(res, 1, ('0'):rep(8-#res))
- return table.concat(res)..' '
- end
- local function binstr(str)
- return str:gsub('.', b)
- end
- local channel = {}
- channel.sock = socket.udp()
- channel.sock:settimeout(0)
- channel.sock:setsockname('*', 3480)
- --[[local s = parser.encode{
- flags = { --flags:
- fp = true, -- first packet
- sp = true, -- send/receive flag
- ld = true, -- confirm
- sm = true -- service message
- },
- id = math.random(999), -- id of message (x32, 4chars)
- count = 44, -- count of parts in message (x32, 4chars)
- part = 33, -- current part number (x32, 4chars)
- window = 2, -- data-travel window size (N msgs per loop, x8, 1char)
- hash = 111, -- hash of message/part (x32, 4chars)
- data = 'Love' -- data of message ( (MTU - 23) chars)
- }
- parser.decode(s)
- ]]
- local message = {}
- message.encode = parser.encode
- message.decode = parser.decode
- message.__index = message
- message.conf = {
- window = 8,
- len = 50,
- disc = .6,
- retry = 30,
- }
- local function randstr(n)
- n = n or 10
- local res = {}
- for i = 1, n do
- res[i] = string.char(math.random(42, 126))
- end
- return table.concat(res)
- end
- function message:build(data)
- local len = self.conf.len
- local o = {}
- o.id = math.random(4228250624)
- o.parts = {}
- o.count = math.ceil(#data/len)
- o.flags = {
- fp = true,
- sp = true,
- ld = false,
- sm = false
- }
- o.window = self.conf.window
- for i = 1, o.count do
- table.insert(o.parts, data:sub((i-1)*len + 1, i * len))
- end
- o.hash = hash(data)
- o.data = ''
- return o
- end
- function message:call(fp, udp, ip, port) -- first packet
- local isSend = type(fp) == 'string'
- local o = isSend and self:build(fp) or fp
- o.parts = o.parts or {} -- list of all parts
- o.flags.fp = false
- o.flags.sp = false
- o.flags.ld = true
- if not isSend then
- o.parts[fp.part] = fp.data
- end
- o.part = 0
- o.update = isSend and self.sw or self.rw -- update function
- o.timer = isSend and self.conf.disc/2 or self.conf.disc
- o.retry = self.conf.retry
- o.destroy = false -- destroy flag
- o.data = ''
- o.udp = udp
- o.ip = ip
- o.port = port
- o.bytes = 0
- o.spdtimer = 1
- o.spd = 0
- setmetatable(o, self)
- if isSend then
- o:sendwindow()
- else
- o:sendto(o:encode())
- end
- return o
- end
- setmetatable(message, {__call = message.call})
- function log(text, ...)
- local file, err = io.open('c:/bin/error_or_not.txt', 'a')
- file:write((... and text:format(...) or text)..'\r\n')
- file:close()
- end
- function message:sendwindow()
- self.flags.sp = true
- printf(self.id..' sendwindow')
- for i = 1, self.window do
- self.flags.fp = self.part == 0
- self.flags.ld = self.window == i or self.part == self.count or self.part == 0
- if self.part < self.count then
- self.part = self.part + 1
- self.data = self.parts[self.part]
- printf('send window', self.part, self.data)
- end
- self:sendto(self:encode())
- end
- end
- function message:updateTimers(dt, bytes)
- self.spdtimer = self.spdtimer - dt
- self.timer = self.timer - dt
- if self.spdtimer < 0 then
- self.spd = self.bytes
- self.bytes = 0
- self.spdtimer = 1
- end
- -- calc received data speed
- if bytes then
- self.timer = self.flags.sp and self.conf.disc/10 or self.conf.disc
- self.retry = self.conf.retry
- self.bytes = self.bytes + bytes
- return
- end
- if self.timer < 0 then
- if self.retry > 1 then
- self.timer = self.conf.disc
- self.flags.ld = true
- self:sendto(self:encode())
- self.retry = self.retry - 1
- else
- self.destroy = true
- end
- end
- end
- function message:sw(dt, data)
- self:updateTimers(dt)
- if not data or not data.flags.ld then return end
- if self.part > data.part then
- printf(self.id..' BACKTRACK!! '..self.part..' '..data.part)
- self.part = data.part
- end
- -- it calls only if data received (receiver confirm received packets)
- if self.part == self.count then
- self.destroy = true
- return
- else
- self:sendwindow()
- end
- end
- function message:rw(dt, data)
- self:updateTimers(dt, data and #data.data)
- if not data or data.part < 1 then return end
- self.parts[data.part] = data.data
- -- if sender used 'need confirm'-flag, send confirm
- if data.flags.ld then
- self.part = #self.parts
- self:sendto(self:encode())
- end
- if #self.parts == self.count then
- self:sendto(self:encode())
- self.part = self.count
- self.retry = 5
- self.update = self.rwo
- self.data = table.concat(self.parts)
- self.parts = {}
- if hash(self.data) ~= self.hash then error('Hash not confirmed! '..hash(self.data)..' :: '..self.hash) end
- return true
- end
- end
- function message:rwo(dt, data)
- self.data = ''
- if data then self:sendto(self:encode()) end
- self:updateTimers(dt)
- end
- function message:sendto(data)
- self.udp:sendto(data, self.ip, self.port)
- end
- local channel = {}
- channel.__index = channel
- function channel:call(udp)
- local o = {}
- o.udp = udp
- o.spool = {}
- o.rpool = {}
- o.time = socket.gettime
- o.timer = o.time()
- o.delta = 0
- return setmetatable(o, {__index = self})
- end
- setmetatable(channel, {__call = channel.call})
- function channel:updateTime()
- local t = self.time()
- self.delta = t - self.timer
- self.timer = t
- return self.delta
- end
- function channel:send(msg, ip, port)
- local msg = message(msg, self.udp, ip, port)
- self.spool[msg.id] = msg
- end
- function channel:receive()
- local dt = self:updateTime()
- return function()
- local data, ip, port = self.udp:receivefrom()
- while data and ip and port do
- local msg = parser.decode(data)
- if not msg then return data, ip, port end
- local pool = msg.flags.sp and self.rpool or self.spool
- if pool[msg.id] then
- printf('Msg %s updated', msg.id)
- if pool[msg.id]:update(dt, msg) then
- return pool[msg.id].data, pool[msg.id].ip, pool[msg.id].port
- end
- elseif msg.flags.ld and msg.flags.sp then
- msg = message(msg, self.udp, ip, port)
- pool[msg.id] = msg
- end
- data, ip, port = self.udp:receivefrom()
- end
- self:updatePool(self.spool, dt)
- local c = self:updatePool(self.rpool, dt)
- if c then return c.data, c.ip, c.port end
- end
- end
- function channel:updatePool(pool, dt)
- for k, v in pairs(pool) do
- if v.update(v, dt) then
- return v
- end
- if v.destroy then
- printf('Msg %s destroyed', k)
- pool[k] = nil
- end
- end
- end
- local u1 = udp()
- u1:settimeout(0)
- u1:setsockname('*', 4444)
- ch1 = channel(u1)
- local u2 = udp()
- u2:settimeout(0)
- u2:setsockname('*', 5555)
- ch2 = channel(u2)
- --ch1:send(msg, '127.0.0.1', 5555)
- --ch1:send(msg, '127.0.0.1', 5555)
- function timered(f, delay)
- local t = socket.gettime()
- return function(...)
- if socket.gettime() - t > delay then f(...) end
- end
- end
- function upd1()
- for data, ip, port in ch1:receive() do
- local file = io.open('c:/bin/textfile.txt', 'wb')
- file:write(data)
- file:close()
- end
- end
- function upd2()
- for data, ip, port in ch2:receive() do
- print('RCV: '..data)
- end
- end
- u1 = timered(upd1, 0)
- u2 = timered(upd2, 0)
- function love.update()
- u1()
- u2()
- end
- local t = {
- parts = 1,
- flags = 1,
- ip = 1,
- port = 1,
- hash = 1,
- -- ftimer = 1,
- -- id = 1,
- window = 1,
- destroy = 1,
- -- data = 1,
- udp = 1,
- update = 1,
- }
- if os.getenv('COMPUTERNAME') == 'FINN' then
- ch1:send('Data: ['..randstr(12565)..']', '192.168.1.3', 5555)
- -- ch1:send('Data: ['..randstr(12565000)..']', '192.168.1.3', 5555)
- -- ch1:send('Data: ['..randstr(12565000)..']', '192.168.1.3', 5555)
- end
- function love.draw()
- if love.keyboard.isDown('1') then
- ch1:send('Data: ['..randstr(math.random(520, 650))..']', '127.0.0.1', 5555)
- end
- love.graphics.print(tableprinter(ch1.spool, t))
- love.graphics.print(tableprinter(ch2.rpool, t), 500)
- local a, b = 0, 0
- for _ in pairs(ch1.spool) do a = a + 1 end
- for _ in pairs(ch2.rpool) do b = b + 1 end
- love.window.setTitle(a..' :: '..b)
- if love.keyboard.isDown('space') then debug.debug() end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement