lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



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/