threading

Chris Angelico rosuav at gmail.com
Thu Apr 10 13:08:49 EDT 2014


On Fri, Apr 11, 2014 at 2:25 AM, Marko Rauhamaa <marko at pacujo.net> wrote:
>> I don't know Python's asyncio as it's very new and I haven't yet found
>> an excuse to use it, but with Pike, I just engage backend mode, set
>> callbacks on the appropriate socket/file/port objects, and let things
>> happen perfectly.
>
> That "set callbacks" and "let things happen" is the hard part. The
> framework part is trivial.

Maybe. Here's a simple self-contained Pike program that makes a simple
echo server - whatever comes in goes out again:


//Create the port (listening connection).
object mainsock=Stdio.Port(12345,accept_callback);

void accept_callback()
{
    //Get the newly-connected socket
    object sock=mainsock->accept();
    //Set up its callbacks
    sock->set_nonblocking(read_callback, write_callback, close_callback);
    //Keep track of metadata (here that'll just be the write buffer)
    sock->set_id((["sock":sock]));
}

//Attempt to write some text, buffering any that can't be written
void write(mapping info, string text)
{
    if (!text || text=="") return;
    if (info->write_me)
    {
        //There's already buffered text. Queue this text too.
        info->write_me += text;
        return;
    }
    int written = info->sock->write(text);
    if (written < 0)
    {
        //Deal with write errors brutally by closing the socket.
        info->sock->close();
        return;
    }
    info->write_me = text[written..];
}

//When more can be written, write it.
void write_callback(mapping info) {write(info, m_delete(info,"write_me"));}

void read_callback(mapping info, string data)
{
    //Simple handling: Echo the text back with a prefix.
    //Note that this isn't line-buffered or anything.
    write(info, ">> " + data);
}

//Not strictly necessary, but if you need to do something when a client
//disconnects, this is where you'd do it.
void close_callback(mapping info)
{
    info->sock = "(disconnected)";
}

//Engage backend mode.
int main() {return -1;}



Setting callbacks? One line. There's a little complexity to the "write
what you can, buffer the rest", but if you're doing anything even a
little bit serious, you'll just bury that away in a mid-level library
function. The interesting part is in the read callback, which does the
actual work (in this case, it just writes back whatever it gets). And
here's how easy it is to make it into a chat server: just replace the
read and close callbacks with these:

multiset(mapping) sockets=(<>);
void read_callback(mapping info, string data)
{
    //Simple handling: Echo the text back with a prefix.
    //Note that this isn't line-buffered or anything.
    sockets[info] = 1;
    write(indices(sockets)[*], ">> " + data);
}

//Not strictly necessary, but if you need to do something when a client
//disconnects, this is where you'd do it.
void close_callback(mapping info)
{
    info->sock = "(disconnected)";
    sockets[info] = 0;
}


If you want to handle more information (maybe get users to log in?),
you just stuff more stuff into the info mapping (it's just like a
Python dict). Handling of TELNET negotiation, line buffering, etc,
etc, can all be added between this and the user-level code - that's
what I did with the framework I wrote for work. Effectively, you just
write one function (I had it double as the read and close callbacks
for simplicity), put a declaration down the bottom to say what port
number you want (hard coded to 12345 in the above code), and
everything just happens. It really isn't hard to get callback-based
code to work nicely if you think about what you're doing.

I expect it'll be similarly simple with asyncio; does someone who's
worked with it feel like implementing similar functionality?

ChrisA



More information about the Python-list mailing list