├── LICENSE ├── README.md ├── examples └── async_pop3.lua ├── lua ├── async_socket.lua └── async_socket │ └── timer.lua ├── rockspecs └── luasocket-async-scm-0.rockspec └── test ├── delay_send_cli.lua ├── delay_send_srv.lua ├── recv_cli.lua └── recv_srv.lua /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Alexey Melnichuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lua-socket-async 2 | ================ 3 | 4 | Asyncronus wrapper around LuaSocket library 5 | 6 | The idea is taken from [lua-memcached](https://github.com/silentbicycle/lua-memcached) 7 | 8 | #API 9 | 10 | ## tcp_client(idle) 11 | 12 | Create tcp client socket. 13 | 14 | ## udp_client(idle) 15 | 16 | Create udp client socket. 17 | 18 | # Common Socket methods 19 | 20 | ## local_host() 21 | 22 | ## local_port() 23 | 24 | ## remote_host() 25 | 26 | ## remote_port() 27 | 28 | ## set_timeout(value) 29 | 30 | ## timeout() 31 | 32 | # TCP Socket methods 33 | 34 | ## connect(timeout, host, port) 35 | 36 | Connect the socket to the specified host/port. 37 | 38 | ## disconnect() 39 | 40 | Disconnect the socket. 41 | 42 | ## bind(host, port) 43 | 44 | Binds the socket to the specified host/port. 45 | 46 | ## send(timeout, msg [,i [,j]]) 47 | 48 | ## recv(timeout [,pattern]) 49 | 50 | # UDP Socket methods 51 | 52 | ## connect(host, port) 53 | 54 | Set peername for socket. 55 | 56 | ## disconnect() 57 | 58 | Clear peername on the socket. 59 | 60 | ## bind(host, port) 61 | 62 | Set sockname on the socket. 63 | 64 | ## send(timeout, msg [,i [,j]]) 65 | 66 | ## recv(timeout [,size]) 67 | 68 | ## sendto(timeout, msg, host, port) 69 | 70 | ## recvfrom(timeout [,size]) 71 | 72 | ##Usage 73 | 74 | ``` Lua 75 | local async_socket = require "async_socket" 76 | 77 | -- 78 | local cnt = 0 79 | function idle() cnt = cnt + 1 end 80 | 81 | local cnn = async_socket.tcp_client(idle) 82 | 83 | local ok = assert(cnn:connect(nil, HOST, PORT)) 84 | 85 | print("CONNECT:",cnt) 86 | 87 | cnn:send(5, MESSAGE) 88 | 89 | print("SEND:",cnt) 90 | 91 | MESSAGE = cnn:recv(5) 92 | 93 | print("RECV:", cnt) 94 | 95 | cnn:close() 96 | ``` 97 | 98 | Using async socket with [lua-pop3](https://github.com/moteus/lua-pop3) library - [async_pop3.lua](/examples/async_pop3.lua) 99 | -------------------------------------------------------------------------------- /examples/async_pop3.lua: -------------------------------------------------------------------------------- 1 | local pop3 = require "pop3" 2 | local async_socket = require "async_socket" 3 | 4 | local host = "127.0.0.1" 5 | local port = 110 6 | 7 | local user = "test@mail.local" 8 | local pass = "123456" 9 | 10 | function create_cnn(idle, idle_timeout, cnn_timeout) 11 | return function(host, port) 12 | local cnn = async_socket.tcp_client(idle) 13 | if idle_timeout then cnn:set_timeout(idle_timeout) end 14 | 15 | local timeout -- io timeout 16 | local ok, err = cnn:connect(cnn_timeout, host, port) 17 | if not ok then return nil, err end 18 | return { 19 | send = function (self, ...) return cnn:send(timeout, ...) end; 20 | receive = function (self, ...) return cnn:recv(timeout, ...) end; 21 | close = function () return cnn:close() end; 22 | settimeout = function (self, val) timeout = val end; 23 | } 24 | end 25 | end 26 | 27 | local cnt = 0 28 | local function counter() cnt = cnt + 1 end 29 | 30 | local mbox = pop3.new(create_cnn(counter, 10000)) 31 | 32 | print(mbox:open(host, port, 5000)) 33 | 34 | print("Open counter:", cnt) 35 | 36 | print(mbox:auth(user, pass)) 37 | 38 | print("Auth counter:", cnt) 39 | 40 | for id, msg in mbox:messages() do 41 | 42 | end 43 | 44 | mbox:close() 45 | 46 | print("Total counter:", cnt) 47 | -------------------------------------------------------------------------------- /lua/async_socket.lua: -------------------------------------------------------------------------------- 1 | local socket = require "socket" 2 | local timer = require "async_socket.timer" 3 | 4 | local DEBUG = false 5 | local function trace(...) 6 | if DEBUG then print(...) end 7 | end 8 | 9 | --- 10 | -- все задержки в мс 11 | 12 | local function async_tcp_connect(sock, host, port, timeout, defer_hook, defer_interval) 13 | local ok, err = sock:connect(host, port) 14 | if ok then return ok 15 | elseif err == "timeout" then 16 | defer_interval = defer_interval and (defer_interval / 1000) or 0 17 | local cnnt = {sock} 18 | local rtimer 19 | if timeout then 20 | rtimer = timer:new() 21 | rtimer:set_interval(timeout) 22 | rtimer:start() 23 | end 24 | while true do 25 | local r,s,err = socket.select(cnnt, cnnt, defer_interval) 26 | if s[1] or r[1] then return s[1] or r[1] end 27 | if err == 'timeout' then 28 | if timeout and rtimer:rest() == 0 then 29 | return nil, 'timeout' 30 | end 31 | local err = sock:getoption("error") 32 | if err then return nil, err end 33 | if defer_hook() == 'break' then return nil, 'break' end 34 | end 35 | end 36 | end 37 | end 38 | 39 | local function async_tcp_receive(sock, spec, timeout, defer_hook) 40 | spec = spec or '*l' 41 | local rtimer 42 | if timeout then 43 | rtimer = timer:new() 44 | rtimer:set_interval(timeout) 45 | rtimer:start() 46 | end 47 | local prefix = '' 48 | while true do 49 | local data, err, rest = sock:receive(spec, prefix) 50 | if not data then prefix = rest end 51 | if data then return data 52 | elseif err == "timeout" then 53 | if timeout and rtimer:rest() == 0 then 54 | return nil, 'timeout', prefix 55 | end 56 | if defer_hook() == 'break' then return nil, 'break', prefix end 57 | else 58 | return nil, err, prefix 59 | end 60 | end 61 | end 62 | 63 | local function async_tcp_send(sock, msg, i, j, timeout, defer_hook) 64 | i, j = i or 1, j or #msg 65 | local rtimer 66 | if timeout then 67 | rtimer = timer:new() 68 | rtimer:set_interval(timeout) 69 | rtimer:start() 70 | end 71 | while true do 72 | local ok, err, last_i = sock:send(msg,i,j) 73 | if last_i then assert( 74 | (last_i >= i and last_i <= j) 75 | or (last_i == i - 1) 76 | ) 77 | else assert(ok == j) end 78 | if ok then return ok 79 | elseif err ~= "timeout" then 80 | return nil, err, j 81 | else 82 | if timeout and rtimer:rest() == 0 then 83 | return nil, 'timeout', last_i 84 | end 85 | i = last_i + 1 86 | if defer_hook() == 'break' then return nil, 'break', i end 87 | end 88 | end 89 | end 90 | 91 | local function async_udp_receive(sock, method, size, timeout, defer_hook) 92 | local rtimer 93 | if timeout then 94 | rtimer = timer:new() 95 | rtimer:set_interval(timeout) 96 | rtimer:start() 97 | end 98 | 99 | while true do 100 | local ok, err, param = sock[method](sock, size) 101 | if ok then 102 | if err then return ok, err, param end 103 | return ok 104 | end 105 | if err ~= 'timeout' then return nil, err end 106 | if timeout and rtimer:rest() == 0 then 107 | return nil, 'timeout' 108 | end 109 | if defer_hook() == 'break' then return nil, 'break' end 110 | end 111 | end 112 | 113 | local function async_udp_send(sock, method, msg, timeout, defer_hook, ...) 114 | -- In UDP, the send method never blocks and the only way it can fail is if the 115 | -- underlying transport layer refuses to send a message to the 116 | -- specified address (i.e. no interface accepts the address). 117 | return sock[method](sock, msg, ...) 118 | end 119 | 120 | local TIMEOUT_MSEC = 1000 121 | local TIMEOUT_SEC = 1 122 | 123 | --- 124 | -- все задержки в сек 125 | 126 | ---------------------------------------------- 127 | local BASE_TRANSPORT = {} do 128 | BASE_TRANSPORT.__index = BASE_TRANSPORT 129 | 130 | function BASE_TRANSPORT:new(idle_hook) 131 | local t = setmetatable({ 132 | user_data = nil; 133 | private_ = { 134 | idle = idle_hook; 135 | timeout = idle_hook and 0 or nil; 136 | local_param = {}; 137 | remote_param = {}; 138 | connected = nil; 139 | } 140 | }, self) 141 | return t 142 | end 143 | 144 | function BASE_TRANSPORT:close() 145 | if self.private_.cnn then 146 | self.private_.cnn:close() 147 | self.private_.cnn = nil 148 | self.private_.connected = nil 149 | end 150 | end 151 | 152 | function BASE_TRANSPORT:is_closed() 153 | return self.private_.cnn == nil 154 | end 155 | 156 | function BASE_TRANSPORT:is_connected() 157 | return self.private_.connected == true 158 | end 159 | 160 | function BASE_TRANSPORT:idle() return self.private_.idle() end 161 | 162 | function BASE_TRANSPORT:is_async() return self.private_.idle ~= nil end 163 | 164 | local function get_host_port(self, fn) 165 | if not self.private_.cnn then return nil, 'closed' end 166 | local ok, err = self.private_.cnn[fn](self.private_.cnn) 167 | if not ok then 168 | if err == 'closed' then self:close() end 169 | return nil,err 170 | end 171 | return ok, err 172 | end 173 | 174 | function BASE_TRANSPORT:local_host() 175 | if self:is_closed() then return nil, 'closed' end 176 | if not self.private_.local_param.host then 177 | local host, port = get_host_port(self,"getsockname") 178 | if not host then 179 | return nil,port 180 | end 181 | self.private_.local_param.host = host; 182 | self.private_.local_param.port = port; 183 | end 184 | return self.private_.local_param.host 185 | end 186 | 187 | function BASE_TRANSPORT:local_port() 188 | if self:is_closed() then return nil, 'closed' end 189 | if not self.private_.local_param.host then 190 | local host, port = get_host_port(self,"getsockname") 191 | if not host then 192 | return nil,port 193 | end 194 | self.private_.local_param.host = host; 195 | self.private_.local_param.port = port; 196 | end 197 | return self.private_.local_param.port 198 | end 199 | 200 | function BASE_TRANSPORT:remote_host() 201 | if self:is_closed() then return nil, 'closed' end 202 | if not self.private_.remote_param.host then 203 | local host, port = get_host_port(self,"getpeername") 204 | if not host then 205 | return nil,port 206 | end 207 | self.private_.remote_param.host = host; 208 | self.private_.remote_param.port = port; 209 | end 210 | return self.private_.remote_param.host 211 | end 212 | 213 | function BASE_TRANSPORT:remote_port() 214 | if self:is_closed() then return nil, 'closed' end 215 | if not self.private_.remote_param.host then 216 | local host, port = get_host_port(self,"getpeername") 217 | if not host then 218 | return nil,port 219 | end 220 | self.private_.remote_param.host = host; 221 | self.private_.remote_param.port = port; 222 | end 223 | return self.private_.remote_param.port 224 | end 225 | 226 | function BASE_TRANSPORT:set_timeout(value) 227 | if self:is_closed() then return nil, 'closed' end 228 | 229 | -- этот timeout используется в асинхронном режиме. 230 | -- Он означает как часто вызывать idle_hook. 231 | -- в синхронном он перезаписывается при каждой операции В/В 232 | if value then value = value * TIMEOUT_SEC end 233 | 234 | local ok,err = self.private_.cnn:settimeout(value) 235 | if not ok then 236 | self:close() 237 | return nil,err 238 | end 239 | self.private_.timeout = value 240 | return value 241 | end 242 | 243 | function BASE_TRANSPORT:timeout() 244 | if self.private_.timeout then 245 | return self.private_.timeout / TIMEOUT_SEC 246 | end 247 | end 248 | 249 | function BASE_TRANSPORT:on_closed() 250 | return self:disconnect() 251 | end 252 | 253 | end 254 | ---------------------------------------------- 255 | 256 | ---------------------------------------------- 257 | local TCP_TRANSPORT = setmetatable({}, BASE_TRANSPORT) do 258 | TCP_TRANSPORT.__index = TCP_TRANSPORT 259 | 260 | local function init_socket(self) 261 | if self.private_.cnn then return self.private_.cnn end 262 | local cnn, err = socket.tcp() 263 | if not cnn then return nil, err end 264 | if self:is_async() then 265 | local ok, err = cnn:settimeout(self.private_.timeout) 266 | if not ok then 267 | cnn:close() 268 | return nil, err 269 | end 270 | end 271 | self.private_.cnn = cnn 272 | return cnn 273 | end 274 | 275 | function TCP_TRANSPORT:bind(host,port) 276 | local cnn, err = init_socket(self) 277 | if not cnn then return nil,err end 278 | 279 | local ok,err = cnn:bind(host or "*", port) 280 | if not ok then 281 | self:close() 282 | return nil,err 283 | end 284 | 285 | self.private_.local_param.host = nil; 286 | self.private_.local_param.port = nil; 287 | 288 | return true; 289 | end 290 | 291 | function TCP_TRANSPORT:connect(timeout, host, port) 292 | local cnn, err = init_socket(self) 293 | if not cnn then return nil,err end 294 | 295 | local ok, err 296 | if self:is_async() then 297 | ok, err = async_tcp_connect(cnn, host, port, timeout, self.private_.idle) 298 | else 299 | cnn:settimeout(timeout) 300 | ok,err = cnn:connect(host, port) 301 | end 302 | if not ok then 303 | if err == "closed" then self:on_closed() end 304 | return ok,err 305 | end 306 | 307 | self.private_.remote_param.host = nil; 308 | self.private_.remote_param.port = nil; 309 | 310 | return true 311 | end 312 | 313 | function TCP_TRANSPORT:disconnect() 314 | return self:close() 315 | end 316 | 317 | function TCP_TRANSPORT:recv_async_impl(timeout, spec) 318 | if timeout then timeout = timeout * TIMEOUT_MSEC end 319 | local ok, err, msg = async_tcp_receive( 320 | self.private_.cnn, 321 | spec, timeout, 322 | self.private_.idle 323 | ) 324 | if err == "closed" then self:on_closed() end 325 | return ok, err, msg 326 | end 327 | 328 | function TCP_TRANSPORT:recv_sync_impl(timeout, spec) 329 | if timeout then timeout = timeout * TIMEOUT_SEC end 330 | local ok, err = self.private_.cnn:settimeout(timeout) 331 | if not ok then return nil, err end 332 | ok, err, data = self.private_.cnn:receive(spec) 333 | if ok then return ok end 334 | if err == 'closed' then self:close() end 335 | return nil, err, data 336 | end 337 | 338 | function TCP_TRANSPORT:send_async_impl(timeout, msg, i, j) 339 | if timeout then timeout = timeout * TIMEOUT_MSEC end 340 | local ok, err, n = async_tcp_send( 341 | self.private_.cnn, 342 | msg, i, j, timeout, 343 | self.private_.idle 344 | ) 345 | if err == "closed" then self:on_closed() end 346 | return ok, err, n 347 | end 348 | 349 | function TCP_TRANSPORT:send_sync_impl(timeout, msg, i, j) 350 | if timeout then timeout = timeout * TIMEOUT_SEC end 351 | local ok, err = self.private_.cnn:settimeout(timeout) 352 | if not ok then return nil, err end 353 | ok, err = self.private_.cnn:send(msg, i, j) 354 | if ok then return ok end 355 | if err == "closed" then self:on_closed() end 356 | return nil, err 357 | end 358 | 359 | function TCP_TRANSPORT:send(...) 360 | if self:is_closed() then return nil, 'closed' end 361 | if self:is_async() then return self:send_async_impl(...) end 362 | return self:send_sync_impl(...) 363 | end 364 | 365 | function TCP_TRANSPORT:recv(...) 366 | if self:is_closed() then return nil, 'closed' end 367 | if self:is_async() then return self:recv_async_impl(...) end 368 | return self:recv_sync_impl(...) 369 | end 370 | 371 | end 372 | ---------------------------------------------- 373 | 374 | ---------------------------------------------- 375 | local UDP_TRANSPORT = setmetatable({}, BASE_TRANSPORT) do 376 | UDP_TRANSPORT.__index = UDP_TRANSPORT 377 | 378 | local function init_socket(self) 379 | if self.private_.cnn then return self.private_.cnn end 380 | local cnn, err = socket.udp() 381 | if not cnn then return nil, err end 382 | if self:is_async() then 383 | local ok, err = cnn:settimeout(self.private_.timeout) 384 | if not ok then 385 | cnn:close() 386 | return nil, err 387 | end 388 | end 389 | self.private_.cnn = cnn 390 | return cnn 391 | end 392 | 393 | function UDP_TRANSPORT:bind(host,port) 394 | if self:is_connected() then 395 | local ok, err = self:disconnect() 396 | if not ok then return nil, err end 397 | end 398 | 399 | local cnn, err = init_socket(self) 400 | if not cnn then return nil,err end 401 | 402 | self.private_.cnn = cnn 403 | 404 | local ok,err = cnn:setsockname(host or "*", port or 0) 405 | if not ok then 406 | self:close() 407 | return nil,err 408 | end 409 | 410 | if self:is_async() then 411 | ok,err = cnn:settimeout(0) 412 | if not ok then 413 | self:close() 414 | return nil,err 415 | end 416 | end 417 | 418 | return true; 419 | end 420 | 421 | function UDP_TRANSPORT:connect(host,port) 422 | if self:is_connected() then 423 | local ok, err = self:disconnect() 424 | if not ok then return nil, err end 425 | end 426 | 427 | local cnn, err = init_socket(self) 428 | if not cnn then return nil,err end 429 | 430 | local ok,err = self.private_.cnn:setpeername(host, port) 431 | if not ok then return nil,err end 432 | self.private_.remote_param.host = nil 433 | self.private_.remote_param.port = nil 434 | self.private_.connected = true 435 | return true 436 | end 437 | 438 | function UDP_TRANSPORT:disconnect() 439 | local ok,err = self.private_.cnn:setpeername('*') 440 | if not ok then return nil,err end 441 | self.private_.remote_param.host = nil 442 | self.private_.remote_param.port = nil 443 | self.private_.connected = nil; 444 | return true 445 | end 446 | 447 | function UDP_TRANSPORT:recv_async_impl(recv, timeout, size) 448 | if timeout then timeout = timeout * TIMEOUT_MSEC end 449 | local ok, err, param = async_udp_receive(self.private_.cnn, recv, size, timeout, self.private_.idle) 450 | if ok then 451 | if err then return ok, err, param end 452 | return ok 453 | end 454 | if err == "closed" then self:on_closed() end 455 | return nil,err 456 | end 457 | 458 | function UDP_TRANSPORT:recv_sync_impl(recv, timeout, ...) 459 | if timeout then timeout = timeout * TIMEOUT_SEC end 460 | local ok, err = self.private_.cnn:settimeout(timeout) 461 | if not ok then return nil, err end 462 | local param 463 | ok, err, param = self.private_.cnn[recv](self.private_.cnn, ...) 464 | if ok then 465 | if err then return ok, err, param end 466 | return ok 467 | end 468 | if err == "closed" then self:on_closed() end 469 | return nil, err 470 | end 471 | 472 | function UDP_TRANSPORT:recv_impl(...) 473 | if self:is_closed() then return nil, 'closed' end 474 | if self:is_async() then return self:recv_async_impl(...) end 475 | return self:recv_sync_impl(...) 476 | end 477 | 478 | function UDP_TRANSPORT:send_sync_impl(send, timeout, msg, ...) 479 | local ok, err = self.private_.cnn[send](self.private_.cnn, msg, ...) 480 | if ok then return ok end 481 | if err == "closed" then self:on_closed() end 482 | return nil, err 483 | end 484 | 485 | function UDP_TRANSPORT:send_async_impl(send, timeout, msg, ...) 486 | if timeout then timeout = timeout * TIMEOUT_MSEC end 487 | local ok, err = async_udp_send(self.private_.cnn, send, msg, timeout, self.private_.idle, ...) 488 | if ok then return ok end 489 | if err == "closed" then self:on_closed() end 490 | return nil,err 491 | end 492 | 493 | function UDP_TRANSPORT:send_impl(...) 494 | if self:is_closed() then return nil, 'closed' end 495 | if self:is_async() then return self:send_async_impl(...) end 496 | return self:send_sync_impl(...) 497 | end 498 | 499 | function UDP_TRANSPORT:send(...) return self:send_impl("send", ...) end 500 | function UDP_TRANSPORT:sendto(...) return self:send_impl("sendto", ...) end 501 | 502 | function UDP_TRANSPORT:recv(...) return self:recv_impl("receive", ...) end 503 | function UDP_TRANSPORT:recvfrom(...) return self:recv_impl("receivefrom", ...) end 504 | 505 | end 506 | ---------------------------------------------- 507 | 508 | local _M = {} 509 | 510 | _M.tcp_client = function(...) return TCP_TRANSPORT:new(...) end 511 | 512 | _M.udp_client = function(...) return UDP_TRANSPORT:new(...) end 513 | 514 | _M.async_tcp_connect = async_tcp_connect 515 | 516 | _M.async_tcp_receive = async_tcp_receive 517 | 518 | _M.async_tcp_send = async_tcp_send 519 | 520 | _M.async_udp_receive = async_udp_receive 521 | 522 | _M.async_udp_send = async_udp_send 523 | 524 | return _M 525 | -------------------------------------------------------------------------------- /lua/async_socket/timer.lua: -------------------------------------------------------------------------------- 1 | local IS_WINDOWS = (package.config:sub(1,1) == '\\') 2 | 3 | local socket = require "socket" 4 | 5 | local function s_sleep(ms) return socket.sleep(ms / 1000) end 6 | 7 | local function s_clock() return socket.gettime() * 1000 end 8 | 9 | local s_tick, s_tick_elapsed 10 | 11 | if not s_tick then 12 | local ok, winutil = pcall(require, "winutil") 13 | if ok then 14 | s_tick = winutil.get_tick_count 15 | s_tick_elapsed = winutil.get_tick_elapsed 16 | end 17 | end 18 | 19 | if not s_tick and IS_WINDOWS then 20 | local ok, ffi = pcall(require, "ffi") 21 | if ok then 22 | ffi.cdef"uint32_t __stdcall GetTickCount();" 23 | local GetTickCount = ffi.C.GetTickCount 24 | local MAX_DWORD = 0xFFFFFFFF 25 | 26 | s_tick = GetTickCount 27 | s_tick_elapsed = function(t) 28 | local cur = s_tick() 29 | if cur >= t then return cur - t end 30 | return cur + (MAX_DWORD - t) 31 | end 32 | end 33 | end 34 | 35 | if not s_tick and IS_WINDOWS then 36 | local ok, alien = pcall(require, "alien") 37 | if ok then 38 | local kernel32 = assert(alien.load("kernel32.dll")) 39 | local GetTickCount = assert(kernel32.GetTickCount) 40 | GetTickCount:types{abi="stdcall", ret = "uint"} 41 | local MAX_DWORD = 0xFFFFFFFF 42 | 43 | s_tick = GetTickCount 44 | s_tick_elapsed = function(t) 45 | local cur = s_tick() 46 | if cur >= t then return cur - t end 47 | return cur + (MAX_DWORD - t) 48 | end 49 | end 50 | end 51 | 52 | if not s_tick then 53 | s_tick = s_clock 54 | function s_tick_elapsed(t) return s_clock() - t end 55 | end 56 | 57 | --- 58 | -- таймеры 59 | -- сработать в определенное время 60 | -- сработать через определенное время 61 | -- узнать сколько времени прошло 62 | 63 | local timer = {} 64 | 65 | function timer:new(...) 66 | local t = setmetatable({},{__index = self}) 67 | return t:init(...) 68 | end 69 | 70 | function timer:init() 71 | self.private_ = {} 72 | return self 73 | end 74 | 75 | --- 76 | -- Устанавливает абсолютное время для срабатывания 77 | function timer:set_time(tm) 78 | self.private_.time = tm 79 | self.private_.interval = nil 80 | return self 81 | end 82 | 83 | --- 84 | -- Устанавливает переод через который необходимо сработать 85 | function timer:set_interval(interval) 86 | assert(interval == nil or type(interval) == 'number') 87 | self.private_.time = nil 88 | self.private_.interval = interval 89 | return self 90 | end 91 | 92 | function timer:reset() 93 | self.private_.start_tick = nil 94 | self.private_.time = nil 95 | self.private_.interval = nil 96 | end 97 | 98 | function timer:time() 99 | return self.private_.time 100 | end 101 | 102 | function timer:interval() 103 | return self.private_.interval 104 | end 105 | 106 | --- 107 | -- Запускает отсчет 108 | function timer:start() 109 | self.private_.start_tick = s_tick() 110 | self.private_.fire_time = self.private_.time 111 | self.private_.fire_interval = self.private_.interval 112 | return self 113 | end 114 | 115 | --- 116 | -- Возвращает признак запущен ли таймер 117 | function timer:started() 118 | return self.private_.start_tick and true or false 119 | end 120 | 121 | --- 122 | -- Останавливает таймер 123 | function timer:stop() 124 | local elapsed = self:elapsed() 125 | self.private_.start_tick = nil 126 | self.private_.fire_time = nil 127 | self.private_.fire_interval = nil 128 | return elapsed 129 | end 130 | 131 | --- 132 | -- 133 | function timer:restart() 134 | local result = self:stop() 135 | self:start() 136 | return result; 137 | end 138 | 139 | --- 140 | -- Возвращает время от старта 141 | function timer:elapsed() 142 | assert(self:started()) 143 | return s_tick_elapsed(self.private_.start_tick) 144 | end 145 | 146 | --- 147 | -- Возвращает время до момента окончания 148 | -- если перед стартом не были вызваны set_time или set_interval 149 | -- будет всегда возвращатся 0 150 | -- таймер остается в активном состоянии 151 | function timer:rest() 152 | assert(self:started()) 153 | local interval 154 | if self.private_.fire_time then interval = self.private_.fire_time - s_clock() 155 | elseif self.private_.fire_interval then interval = self.private_.fire_interval - self:elapsed() 156 | else return 0 end 157 | 158 | if interval <= 0 then 159 | self.private_.fire_time = nil 160 | self.private_.fire_interval = nil 161 | interval = 0 162 | end 163 | return interval 164 | end 165 | 166 | local M = {} 167 | 168 | M.sleep = s_sleep 169 | M.clock = s_clock 170 | M.tick_count = s_tick 171 | M.tick_elapsed = s_tick_elapsed 172 | 173 | M.new = function (...) return timer:new(...) end 174 | 175 | return M -------------------------------------------------------------------------------- /rockspecs/luasocket-async-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "luasocket-async" 2 | version = "scm-0" 3 | 4 | source = { 5 | url = "https://github.com/moteus/lua-socket-async/archive/master.zip", 6 | dir = "lua-socket-async-master", 7 | } 8 | 9 | description = { 10 | summary = "Async wrapper around LuaSocket", 11 | homepage = "https://github.com/moteus/lua-socket-async", 12 | license = "MIT/X11", 13 | } 14 | 15 | dependencies = { 16 | "lua >= 5.1, < 5.3", 17 | "luasocket", 18 | } 19 | 20 | build = { 21 | copy_directories = {"test"}, 22 | 23 | type = "builtin", 24 | 25 | modules = { 26 | ["async_socket" ] = "lua/async_socket.lua"; 27 | ["async_socket.timer" ] = "lua/async_socket/timer.lua"; 28 | }, 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/delay_send_cli.lua: -------------------------------------------------------------------------------- 1 | local async_socket = require "async_socket" 2 | local timer = require "timer" 3 | 4 | local HOST = arg[1] or '127.0.0.1' 5 | 6 | function test_recv() 7 | print("---- START TEST RECV") 8 | local cnt = 0 9 | function counter() cnt = cnt + 1 end 10 | 11 | local cnn = async_socket.tcp_client(counter) 12 | local ok, err = cnn:connect(nil, HOST, '8042') 13 | print("CONNECT:",ok, err) 14 | print("COUNTER:",cnt) 15 | if not ok then 16 | cnn:close() 17 | return; 18 | end 19 | print("---- CONNECT PASSED") 20 | 21 | print("local_host :", cnn:local_host()) 22 | print("local_port :", cnn:local_port()) 23 | print("remote_host:", cnn:remote_host()) 24 | print("remote_port:", cnn:remote_port()) 25 | 26 | local pt = timer:new() 27 | pt:start() 28 | print("RECV (5 sec):",cnn:recv(5)) 29 | print("RECV (5 sec):",cnn:recv(5)) 30 | print("RECV (5 sec):",cnn:recv(5)) 31 | print("RECV (5 sec):",cnn:recv(5)) 32 | print("RECV (5 sec):",cnn:recv(5)) 33 | print("COUNTER:", cnt) 34 | print("ELAPSED:", pt:elapsed()) 35 | cnn:close() 36 | print("---- RECV PASSED") 37 | end 38 | 39 | test_recv() 40 | -------------------------------------------------------------------------------- /test/delay_send_srv.lua: -------------------------------------------------------------------------------- 1 | local socket = require "socket" 2 | local srv = assert(socket.bind('*', 8042)) 3 | local sock = assert(srv:accept()) 4 | print(sock) 5 | socket.sleep(10) 6 | for i = 1,9 do 7 | sock:send(tostring(i)) 8 | socket.sleep(1) 9 | end 10 | 11 | sock:send( 12 | string.char(10) .. string.char(13) 13 | ) 14 | 15 | sock:close() 16 | -------------------------------------------------------------------------------- /test/recv_cli.lua: -------------------------------------------------------------------------------- 1 | local async_socket = require "async_socket" 2 | local timer = require "timer" 3 | 4 | local HOST = arg[1] or '127.0.0.1' 5 | 6 | function test_send() 7 | print("---- START TEST SEND") 8 | 9 | local cnt = 0 10 | function counter() cnt = cnt + 1 end 11 | 12 | local cnn = async_socket.tcp_client(counter) 13 | local ok, err = cnn:connect(nil, HOST, '8041') 14 | print("CONNECT:",ok, err) 15 | print("COUNTER:",cnt) 16 | if not ok then 17 | cnn:close() 18 | return; 19 | end 20 | print("---- CONNECT PASSED") 21 | 22 | print("local_host :", cnn:local_host()) 23 | print("local_port :", cnn:local_port()) 24 | print("remote_host:", cnn:remote_host()) 25 | print("remote_port:", cnn:remote_port()) 26 | 27 | local pt = timer:new() 28 | pt:start() 29 | print("SEND:", cnn:send(1.5, ("hello"):rep(50000))) 30 | print("COUNTER:", cnt) 31 | print("ELAPSED:", pt:elapsed()) 32 | cnn:close() 33 | print("---- SEND PASSED") 34 | end 35 | 36 | test_send() -------------------------------------------------------------------------------- /test/recv_srv.lua: -------------------------------------------------------------------------------- 1 | local socket = require "socket" 2 | local srv = assert(socket.bind('*', 8041)) 3 | local sock = assert(srv:accept()) 4 | 5 | local len = 0 6 | while true do 7 | local msg, err, post = sock:receive() 8 | len = len + #(msg or post) 9 | if err and err ~= 'timeout' then 10 | if err ~= 'closed' then 11 | print("!!!error:", err) 12 | end 13 | break 14 | end 15 | end 16 | 17 | print("recived:", len) 18 | 19 | sock:close() 20 | srv:close() 21 | --------------------------------------------------------------------------------