LuaLanes和LuaSockets

Cyc*_*nus 6 lua luasocket lua-lanes

我正在研究一个小的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])
Run Code Online (Sandbox Code Playgroud)

laneTest函数的定义如下:

function laneTest (id)
    local client = allClients [id]
    print ('peer: ', client:getpeername())
end
Run Code Online (Sandbox Code Playgroud)

我的问题是在laneTest函数内部,当作为一个通道运行时,我收到这个可爱的错误信息:

尝试索引本地"客户端"(userdata值)

(从线client:getpeername())

所以..我不确定这里发生了什么?车道与插座不兼容,还是我做错了什么?

我想这可能是因为车道附带的版本和Lua的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])
Run Code Online (Sandbox Code Playgroud)
  1. 这无法声明: attempt to call global 'require' (a nil value)
  2. 删除require ('socket')函数内部的行并重试也无法说:attempt to index local 'client' (a userdata value)

我提前道歉,因为错过了显而易见的事,但是......你如何让插座与车道一起工作?

编辑3:

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

据我所知,没有修补luasockets就无法使用Lanes with Sockets.有关详细信息,请参阅此处的讨论; 但简而言之(正如Deco的答案中所解释的那样):通道不适用于userdata.luasocket没有提供任何其他方式来访问套接字/套接字信息.

我不想修补luasocket,因为我宁愿使用车道,我要继续前进并坚持使用copas或couroutines.

谢谢大家!

Dec*_*eco 5

Lua Lanes为每条车道创造了一个全新的(但最小的)Lua状态.传递的任何upvalues或参数都被复制,而不是被引用; 这意味着您正在复制allClients表及其包含的套接字.

发生错误的原因是套接字是userdata,如果没有来自C模块的建议,Lua Lanes不知道如何复制.不幸的是,LuaSocket没有提供这样的建议.(有很多方法可以解决这个问题,但要小心:LuaSocket 不是线程安全的,并且很难跟踪同步错误.)

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

解决方案!

这些都是从易责令硬(大多由我的其他答案转录这里).

单线程轮询

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

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

简单地反复调用它们.我建议使用协程调度程序用于非阻塞版本,以允许程序的其他部分继续执行而不会导致太多延迟.

(如果你选择这个解决方案,我建议回顾保罗的答案.)

双线程轮询

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

这可能是最适合您的选择.

多线程轮询

与上面相同,除了每个线程处理所有客户端的子集(1到1是可能的,但减少的返回将设置为大量的客户端).

我在这里提供了一个简单的例子.它依赖于Lua Lanes 3.4.0(GitHub repo)和修补的LuaSocket 2.0.2(源代码,补丁,博客文章重新补丁)

结果很有希望,但如果你从中得到它,你肯定应该重构我的示例代码.

LuaJIT + ENet

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


Pau*_*nko 5

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

还有copas库将自己描述为"基于可由TCP/IP服务器使用的协同程序的调度程序",但您实际上也可以使用它来异步发送请求(使用addthreadstep调用的组合).