【问题标题】:LuaLanes and LuaSocketsLuaLanes 和 LuaSockets
【发布时间】:2012-11-04 14:50:53
【问题描述】:

我正在开发一个小 Lua 应用程序(在 Lua for Windows 下,如果重要的话),它使用套接字与外界通信。 (LuaSocket)

我正在尝试并行发出多个请求。所以我认为 LuaLanes 是要走的路。 (当然,如果有更好的解决方案,我对替代方案持开放态度,但我不想为此处理协程。)

类似这样的:

server = assert (socket.bind ('*', 1234))
client = server : accept ()
-- set id to some unique value
allClients [id] = client
theLane = lanes.gen ("", laneTest) ( id )
print (theLane [1])

其中laneTest函数定义如下:

function laneTest (id)
    local client = allClients [id]
    print ('peer: ', client:getpeername())
end

我的问题是在 laneTest 函数中,当作为车道运行时,我收到了这个可爱的错误消息:

尝试索引本地“客户端”(用户数据值)

(来自client:getpeername()

所以.. 我不确定这里发生了什么?通道与套接字不兼容,还是我做错了什么?

我想这可能是 Lua for Windows 附带的通道版本是古老的 (luaforwindows) 并且不适用于套接字,但最新版本可能吗? (车道 2.0.4 与最近的 3.xx)

我真的不知道如何更新我拥有的车道版本,否则我现在已经尝试过了,所以。如果那是我可能要去的地方或者有更明显的事情我做错了,我会很感激任何建议。

编辑: 我继续通过 luarocks 安装了通道,使用安装为岩石的通道 3.1.6-1 时遇到了同样的问题。

编辑 2: 试过这个(但仍然失败):

require ('socket')
require ('lanes')
local allClients = {}

function theLane (id)
    print ('the id:', id) -- correctly prints out the id passed to the function
    local SOCKET = require ('socket')
    local client = allClients [id]
    print ('peer:', client:getpeername())
    client : close ()
end

local server = assert (SOCKET.bind ('*', 1234))
local ip, port = server:getsockname ()
local laneFunc = lanes.gen('', theLane)
local client = server:accept ()
allClients [1] = client
local x = laneFunc (1)
print (x[1])
  1. 声明失败:attempt to call global 'require' (a nil value)
  2. 删除函数内的require ('socket') 行并重试也失败说:attempt to index local 'client' (a userdata value)

对于遗漏了显而易见的事情,我提前道歉,但是...如何让套接字与通道一起使用?

编辑 3:

好吧,我正在编辑它以供将来参考:)

据我所知,没有修补 luasocket 就无法使用带有套接字的通道。有关更多信息,请参阅讨论here;但简而言之(正如德科的回答中所解释的那样):车道不适用于用户数据。 luasocket 不提供任何其他访问套接字/套接字信息的方式。

我不想修补 luasocket,就像我宁愿使用车道一样,我会继续坚持使用 copas 或 couroutines。

谢谢大家!

【问题讨论】:

    标签: lua luasocket lua-lanes


    【解决方案1】:

    Programming Lua 在non-preemptive multithreading 上有示例(使用协程),您可能几乎可以直接使用。在我看来,协程将是您用例的更好解决方案。

    还有 copas library 将自己描述为“基于协程的调度程序,可供 TCP/IP 服务器使用”,但实际上您也可以将其用于异步发送请求(使用 addthreadstep 来电)。

    【讨论】:

    • 感谢您的链接,我想我最终可能会为此使用协程(并将研究您的示例中的调度程序模型),但如果我可以使用车道,它看起来更……优雅? “如果”是重要的部分:P
    【解决方案2】:

    Lua 通道为每个通道创建一个全新的(但最小的)Lua 状态。 任何传递的上值或参数都被复制,而不是被引用;这意味着正在复制您的 allClients 表以及它包含的套接字。

    发生错误是因为套接字是用户数据,如果没有来自 C 模块的建议,Lua Lanes 不知道如何复制。不幸的是,LuaSocket 没有提供这样的建议。 (有一些方法可以解决这个问题,但要小心:LuaSocket 不是线程安全的,同步错误很难追踪。)

    虽然它不能解决你的问题,但你应该注意,你需要在你的生成通道中使用 LuaSocket;默认不复制。

    解决方案!

    这些是从易到难排序的(主要是从我的其他答案 here 转录而来)。

    单线程轮询

    在LuaSocket中反复调用轮询函数:

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

    只需重复调用这些。 我建议在非阻塞版本中使用coroutine scheduler,以允许程序的其他部分继续执行而不会造成太多延迟。

    (如果您选择此解决方案,我建议查看Paul's answer。)

    双线程轮询

    您的主线程根本不处理套接字。相反,它会产生另一个需要 LuaSocket 的通道,处理所有客户端并通过 linda 与主线程通信。

    这可能是您最可行的选择。

    多线程轮询

    与上述相同,除了每个线程处理所有客户端的一个子集(1 比 1 是可能的,但随着大量客户端的出现收益递减)。

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

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

    LuaJIT + ENet

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

    【讨论】:

    • Deco:仅供参考,您在解决方案段落中的链接包含 /edit。
    • 感谢您提供全面的解决方案 :) 正如您所描述的,我正在尝试使用双线程或多线程轮询解决方案,但我仍然无法让通道为我工作. (也感谢您指出我需要在车道内使用插座,我不知道这一点)。我将编辑我最初的问题以发布我正在做什么以及它如何失败的示例,如果您有时间看一下,也许我错过了什么?谢谢
    猜你喜欢
    • 2022-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-25
    • 2011-02-17
    相关资源
    最近更新 更多