Adding idle timeout capabilities to asyncore

Josiah Carlson josiah.carlson at gmail.com
Tue Oct 23 11:34:19 EDT 2007


On 22 Ott, 12:28, Giampaolo Rodola' <gne... at gmail.com> wrote:
> Hi there.
> We're talking about an asyncore-based server.
> Just for the heck of it I'd like to set a timeout which will
> disconnects the clients if they're inactive (i.e., no command or data
> transfer in progress) for a long period of time.
> I was thinking about putting the timeout value into an attribute:
>
> def settimeout(self, secs):
>      self.timeout = time.time() + secs
>
> And then have the readable method call time.time() at every loop to
> check if time has passed or not:
>
> def readable(self):
>     if time.time() >= self.timeout:
>         self.send("Timeout")
>         self.close()
>     return 1
>
> My only concern is if it's a good idea calling time.time() so often.
> Since A LOT of clients could be connected simultaneously, couldn't it
> be a too much resource-intensive operation?
> I'd also be curious to know how Twisted implemented this kind of
> stuff.
> By calling time.time() at every loop?
>
> Thanks in advance.

Calling time.time() is relatively inexpensive in comparison to pure
Python function calls, but indeed, it could be a bottleneck.

I don't know what Twisted does, but what I would do is to add two
variables to each instance of the class you want to add timeouts to;
self.timeout and self.lastdata .  self.lastdata would hold the time
for the last time you sent or received data on the socket, and
self.timeout would hold the delay between when you last sent/received
data and when it should be closed due to idle timeout.

Now, since you are smart, you don't need to alter your handle_send()
or handle_receive() methods in your asyncore subclass.  Instead, you
rewrite asyncore.poll() to update .lastdata for every socket that is
in either the reader or writer list, which will result in one
time.time() call.  Further, to check for timeouts, you only ever need
to check those sockets that are *not* in the readable or writable
lists...

To be precise, add the following block to your own copy of
asyncore.poll just after the 'for fd in e:' block...


        #handle timeouts
        rw = set(r) + set(w)
        now = time.time()
        for f in (i for i in rw if i in map):
            map[f].lastdata = now
        for j in (map[i] for i in map if i not in rw):
            if j.timeout+j.lastdata > now:
                #timeout!
                j.handle_close()

You ARE going to need to initialize .timeout and .lastdata members for
every instance, but that shouldn't be so bad (for a socket that
doesn't time out, I would actually suggest a 1 hour or 1 day timeout).

 - Josiah




More information about the Python-list mailing list