6

我正在开发一个使用套接字与外界通信的小型 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)

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

编辑:我继续通过 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:

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

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

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

谢谢大家!

4

2 回答 2

5

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

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

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

解决方案!

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

单线程轮询

LuaSocket中反复调用轮询函数:

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

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

(如果您选择此解决方案,我建议您查看Paul 的回答。)

双线程轮询

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

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

多线程轮询

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

我已经做了一个简单的例子,可以在这里找到。它依赖于 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 的 FFI访问它(推荐)。

于 2012-11-04T20:30:50.350 回答
5

Programming Lua 有关于非抢占式多线程(使用协程)的示例,您几乎可以直接使用它。在我看来,协程将是您用例的更好解决方案。

还有一个copas 库,它把自己描述为“一个基于协程的调度程序,可以被 TCP/IP 服务器使用”,但实际上你也可以使用它来异步发送请求(使用addthreadstep调用的组合)。

于 2012-11-04T22:18:27.203 回答