asyncore based port splitter code questions

Giampaolo Rodola' gnewsg at gmail.com
Fri Jan 8 21:14:02 CET 2010


On 4 Gen, 18:58, George Trojan <george.tro... at noaa.gov> wrote:

> asyncore based code is supposed to be simple,
> but I need while loops and a lot of try/except clauses.

First of all: you DON'T have to use while loops or anything which is
blocking where by "blocking" I mean anything like time.sleep().
asyncore, just like Twisted, is an asynchrounous abstraction on top of
select().
Anything you call must return *immediately* otherwise the whole thing
will hang aka "stop serving any connected client or server".

> I designed the code by looking at Python 2.3
> source for asyncore and originally wanted to use add_channel() and
> del_channel() methods. However in Python 2.6 del_channel() closes the
> socket in addition to deleting it from the map.

Don't look at the 2.3 source. Use asyncore of Python 2.6 which is far
more improved, bug-fixed and also *different*, where by that I mean
that it might actually behaves differently.
If you are forced to use Python 2.3 my advice is to get a copy of
Python's asyncore.py and asynchat.py and include them in your code.

Secondly, to temporarily "sleep" your connections *don't* remove
anything from your map.
The correct way of doing things here is to override readable() and
writable() methods and make them return False as long as you want your
connection to hang.

Now I'm going to comment some parts of your code.

> class Reader(asyncore.dispatcher):
>      def __init__(self, sock, writers):
>          asyncore.dispatcher.__init__(self, sock)
>          self.writers = writers
>
>      def handle_read(self):
>          data = self.recv(1024)
>          for writer in self.writers:
>              writer.add_data(data)
[...]
>      def handle_write(self):
>          while self.data:
>              log_msg('sending data to %s' % str(self.address))
>              sent = self.send(self.data)
>              self.data = self.data[sent:]
>          self.suspend_channel()


By looking at how you are appending data you want to send in a buffer,
it looks like you might want to use asynchat.async_chat rather than
asyncore.dispatcher.
async_chat.push() and async_chat.push_with_producer() methods already
take care of buffers logic and make sure that all the data gets sent
to the other peer without going lost.

Actually there's no reason to use asyncore.dispatcher class directly
except for creating a socket which listens on an interface and then
passes the connection to another class inheriting from
asynchat.async_chat which will actually handle that session.

So my recommendation is to use asynchat.async_chat whenever possible.

>      def suspend_channel(self, map=None):
>          fd = self._fileno
>          if map is None:
>              map = self._map
>          if fd in map:
>              del map[fd]
>

As I said this is unecessary.
Override readable() and writable() methods instead.

>      def handle_close(self):
>          log_msg('closing writer connection')
>          self.close()
>          # try to reconnect
>          time.sleep(TMOUT)
>          self.mksocket()

You really don't want to use time.sleep() there.
It blocks everything.

>          while True:
>              try:
>                  disp = Dispatcher(port, destinations)
>                 asyncore.loop(timeout=TMOUT, use_poll=True)
>              except socket.error, (errno, e):
>                  if errno == 98:
>                      log_msg('sleeping %d s: %s', (30, e))
>                      time.sleep(30)

Same as above.


As a final note I would recommend to take a look at pyftpdlib code
which uses asyncore/asynchat as part of its core:
http://code.google.com/p/pyftpdlib
It can be of some help to figure out how things should be done.



--- Giampaolo
http://code.google.com/p/pyftpdlib/



More information about the Python-list mailing list