我正在开发一个小的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])
attempt to call global 'require' (a nil value)
require ('socket')
行并重试也无法说:attempt to index local 'client' (a userdata value)
我提前道歉,因为错过了明显的问题,但是......你如何让插座与车道一起工作?
编辑3:
好吧,我正在编辑这个以供将来参考:)
据我所知,如果没有修补luasockets,就无法使用带有套接字的Lanes。有关详细信息,请参阅讨论here;但简而言之(正如Deco的答案中所解释的那样):通道不适用于userdata。 luasocket没有提供任何其他方式来访问套接字/套接字信息。
我不想修补luasocket,因为我会更喜欢使用车道,我会继续使用copas或couroutines。
全部谢谢!
答案 0 :(得分:5)
Lua Lanes为每条车道创造了一个全新的(但最小的)Lua状态。 传递的任何upvalues或参数都被复制,而不是被引用;这意味着正在复制allClients表及其包含的套接字。
发生错误是因为套接字是userdata,Lua Lanes在没有来自C模块的建议的情况下不知道如何复制。不幸的是,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)
结果很有希望,但如果你从中得到它,你肯定应该重构我的示例代码。
ENet是一个很棒的图书馆。它提供了TCP和UDP之间的完美组合:在需要时可靠,否则不可靠。它还抽象了操作系统特定的细节,就像LuaSocket一样。您可以使用Lua API绑定它,或通过LuaJIT's FFI直接访问它(推荐)。
答案 1 :(得分:5)
编程Lua在non-preemptive multithreading(使用协同程序)上有例子,您几乎可以直接使用它。在我看来,协程将是一个更好的解决方案。
还有copas library将自己描述为“基于可由TCP / IP服务器使用的协同程序的调度程序”,但您实际上也可以使用它来异步发送请求(使用{{的组合) 1}}和addthread
来电。)