[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: LuaSocket, LTN12 and coroutines: how to? (long post)
- From: Diego Nehab <diego@...>
- Date: Sat, 29 Jan 2005 02:33:20 -0500 (EST)
Hi,
> Server with coroutines HOWTO...
David and I discussed this some time ago. I am currently discussing this
with Carregal too, who is working on an HTTP server called Xavante.
I agree LuaSocket's examples are short on the server side. If you don't
need a concurrent, non-blocking server, you can use the tinyirc.lua
example that comes with LuaSocket. If you need all of the above, then
I am working on a simple T server (something that just dumps
all communication between two sockets to the terminal) and plan
to include it in the next release.
Meanwhile, you should know the following:
* When a server receives a connection request, it will select as readable.
* You should set the timeout value for the server socket to make sure you
won't block forever in case someone cancels the connection request
between your calls to select and accept
* You must set the timeout to 0 in all client sockets you are dealing with.
* receive() and send() were designed to be easy to use in this case.
They return partial results that can be passed back to them when
there is a timeout condition (actually, there is a design "bug" in
the send function that I will fix in the next version, to make it
even simpler to use)
* everytime you want to do I/O, you should do so politely. That is,
the socket has a timeout of 0. If you see a timeout, then yield. You
can use the following driver functions:
function polite_send(client, string)
local start = 1
local sent, ret, err
while true do
ret, err, sent = client:send(string, start)
if err == "timeout" then
start = start+sent -- this is the line that will be
-- simplified away in the next release :)
coroutine.yield(client, "want-to-send")
elseif not ret then return nil, err
else return ret end
end
end
function polite_receive(client, pattern)
local part = ""
local ret, err
while true do
ret, err, part = client:send(string, part)
if err == "timeout" then coroutine.yield(client, "want-to-receive")
elseif not ret then return nil, err
else return ret end
end
end
Depending on the needs of the protocol you are serving, you could use
the following, or an extension to the following:
create a list for sockets that "want-to-receive" and another for
those that "want-to-send"
create a table to hold a map between sockets and coroutines
create a server socket and set its timeout to a small value
put it in the "want-to-receive" table
loop selecting on the "want-to-receive" and "want-to-send" tables
if the server is in the "readable" table returned by select
accept a client and set its timeout to 0
create a coroutine from driver function that knows how to service
client requests (this function has to use polite_send and
polite_receive to do I/O!)
associate this coroutine with the client socket in the map table
resume this newly created coroutine (pass it its socket)
for each client socket that is "readable", resume the associated
coroutine
when resume returns, remove it from the "want-to-receive table"
if it is done, remove it from the map table too
if it is not done, use the second return value to decide if you
should put it in the "want-to-receive" or "want-to-send" table.
for each client socket that is "writable", resume the associated
coroutine
when resume returns, remove it from the "want-to-receive table"
if it is done, remove it from the map table too
if it is not done, use the second return value to decide if you
should put it in the "want-to-receive" or "want-to-send" table.
This is not as complicated as it seems. We are in the process of testing
exactly this idea for a dispatcher in Xavante. If David or Mike would
be kind enough to simplify or validate the above, I would feel even more
confident. :)
> local aServer = aSocket.try( aSocket.bind( "*", 1110 ) )
Beware that the errors that socket.try() throws are only caught by
socket.protect(). You should use assert() for the above line instead. (Let
me know if you found an example of this in the manual so I can fix it).
Regards,
Diego.