[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: LuaSocket, LTN12 and coroutines: how to? (long post)
- From: PA <petite.abeille@...>
- Date: Sat, 29 Jan 2005 16:44:17 +0100
On Jan 29, 2005, at 08:33, Diego Nehab wrote:
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.
Excellent! A more substantial example which covers LTN12 and
"Non-Preemptive Multithreading" would be very much appreciated :))
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
Ok. Now, I wish I could handle the above "polite" send and receive in
terms of LTN12's Sinks and Sources. Would Socket's source do the "right
thing" or do I have to write my own?
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. :)
Thanks! I will try to digest the above now :)
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).
I found the above "try" construct in LuaSocket's introduction:
local server = socket.try(socket.bind("*", 0))
Cheers
--
PA, Onnay Equitursay
http://alt.textdrive.com/