9

在当前的 lua 套接字实现中,我看到我们必须安装一个定期回调的计时器,以便我们检查非阻塞 API 以查看我们是否收到任何内容。

这一切都很好,但是在 UDP 情况下,如果发送者有很多信息正在发送,我们是否有丢失数据的风险。假设另一台设备通过 UDP 发送 2MB 照片,我们每 100 毫秒检查一次套接字接收。在 2MBps 时,在我们的调用查询底层 TCP 堆栈之前,底层系统必须存储 200Kbits。

当我们在特定套接字上接收到数据而不是我们现在必须做的轮询时,有没有办法让事件触发?

4

4 回答 4

13

有多种方法可以处理此问题;你会选择哪一个取决于你想做多少工作。*

但首先,您应该(对自己)澄清您是在处理 UDP 还是 TCP;UDP 套接字没有“底层 TCP 堆栈”。此外,UDP 是用于发送整个数据(如文本或照片)的错误协议;它是一个不可靠的协议,因此不能保证您接收到每个数据包,除非您使用托管套接字库(例如ENet)。

Lua51/LuaJIT + LuaSocket

轮询是唯一的方法。

  • 阻塞:socket.select不带时间参数的调用并等待套接字可读。
  • 非阻塞:socket.select使用超时参数调用0,并sock:settimeout(0)在您正在读取的套接字上使用。

然后简单地重复调用这些。我建议对非阻塞版本使用协程调度程序,以允许程序的其他部分继续执行而不会造成太多延迟。

Lua51/LuaJIT + LuaSocket + Lua Lanes(推荐)

与上述方法相同,但套接字存在于使用Lua Lanes制作的另一个通道(另一个线程中的轻量级 Lua 状态)中(最新来源)。这使您可以立即从套接字读取数据并进入缓冲区。然后,您使用linda将数据发送到主线程进行处理。

这可能是您问题的最佳解决方案。

我已经做了一个简单的例子,可以在这里找到。它依赖于 Lua Lanes 3.4.0 ( GitHub repo ) 和修补过的 LuaSocket 2.0.2 ( source , patch , blog post re' patch )

结果是有希望的,但如果你从中派生出我的示例代码,你肯定应该重构它。

LuaJIT + 操作系统特定的套接字

如果你有点自虐,你可以尝试从头实现一个套接字库。LuaJITFFI 库使这从纯 Lua 成为可能。Lua Lanes 对此也很有用。

对于 Windows,我建议查看William Adam 的博客。他在 LuaJIT 和 Windows 开发方面有过一些非常有趣的冒险经历。至于 Linux 和其他,请查看 C 教程或 LuaSocket 的源代码,并将它们转换为 LuaJIT FFI 操作。

(如果 API 需要,LuaJIT 支持回调;但是,与从 Lua 轮询到 C 相比,存在显着的性能成本。)

LuaJIT + ENet

ENet是一个很棒的库。它提供了 TCP 和 UDP 之间的完美组合:需要时可靠,否则不可靠。它还抽象了操作系统特定的细节,就像 LuaSocket 一样。你可以使用 Lua API 来绑定它,或者直接通过 LuaJIT 的 FFI 访问它(推荐)。

*双关语无意。

于 2012-10-15T07:18:58.543 回答
5

我将 lua-ev https://github.com/brimworks/lua-ev用于所有 IO 多路复用的东西。它非常易于使用,适合 Lua(及其function),就像魅力一样。它是基于 select/poll/epoll 或 kqueue 的,并且性能也非常好。

 local ev = require'ev'
 local loop = ev.Loop.default
 local udp_sock -- your udp socket instance
 udp_sock:settimeout(0) -- make non blocking
 local udp_receive_io = ev.IO.new(function(io,loop)
       local chunk,err = udp_sock:receive(4096)
       if chunk and not err then
           -- process data
       end
    end,udp_sock:getfd(),ev.READ)

 udp_receive_io:start(loop) 
 loop:loop() -- blocks forever

在我看来,Lua+luasocket+lua-ev 只是构建高效和健壮的网络应用程序(用于嵌入式设备/环境)的梦之队。还有更强大的工具!但是如果你的资源有限,Lua 是一个不错的选择!

于 2012-10-19T08:07:13.827 回答
2

Lua 本质上是单线程的;没有所谓的“事件”。没有办法中断 Lua 代码的执行。因此,虽然您可以装配一些看起来像事件的东西,但只有当您调用一个轮询哪些事件可用的函数时,您才能得到一个。

通常,如果您尝试使用 Lua 进行此类低级工作,那么您使用的是错误的工具。您应该使用 C 或其他东西来访问此类数据,然后在准备好时将其传递给 Lua。

于 2012-10-15T04:50:07.017 回答
1

您可能正在使用非阻塞select()来“轮询”套接字以获取任何可用的新数据。Luasocket 没有提供任何其他接口来查看是否有新数据可用(据我所知),但是如果您担心每秒执行 10 次会花费太多时间,请考虑编写简化版本只检查您需要的一个套接字,并避免创建和丢弃 Lua 表。如果这不是一个选项,请考虑传递nil给那些您不需要读取的列表而不是传递静态表而不是临时表select(){}

local rset = {socket}
... later
...select(rset, nil, 0)

代替

...select({socket}, {}, 0)
于 2012-10-15T05:56:54.547 回答