Normally when you make a nonblocking connect, socket gets EINPROGRESS, later the result of this request will appear after select returns.
 
I think asyncore/asynchat incorrectly interprets asychronous connects. Reading Steven's book, I did some modifications to asyncore.dispatcher and asynchat.async_chat as below to correctly interpret errors like: ECONNREFUSED, ETIMEDOUT, ENETUNREACH, EHOSTUNREACH, ENETDOWN. Then I used my custom base classes. In the code below I only show the approach for this situation. I have other modifications for other problems (infinte recursions etc.) but those are not shown here.
 
The reason is that when a socket connection makes "connect", the descriptor becomes both readable and writable at the same time if there is a problem in "connect". However, asyncore polling mechanism gets "readable ready" and "writable ready" separately. Therefore, it cannot detect "connect" errors. If you consider writability as a healthy condition (incorrectly) you would try to write without a connection establishment, this gives some other error but not the actual error, in handle_error().
 
Full discussion of nonblocking connects can be found in chapter 16 of the book "UNIX Network Programming Volume 1, Third Edition The Sockets Networking API, W. Richard Stevens, Bill Fenner, Andrew M. Rudoff"
 
Here is my approach:
 
# drive from asyncore.dispatcher:
class base_channel(asyncore.dispatcher):
   
    def handle_read_event(self):
        if self.accepting:
            if not self.connected:
                self.connected = 1
            self.handle_accept()
        elif not self.connected:
            self.handle_connect_event()
        else:
            self.handle_read()
 
    def handle_write_event(self):
        if not self.connected:
            self.handle_connect_event()
        else:
            self.handle_write()
 
    def handle_connect_event(self):
        err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
        if err:
            raise socket.error, (err, os.strerror(err))
        self.connected = 1
        self.handle_connect()
 
# I mix the above base_channel

class base_chatter(base_channel, asynchat.async_chat):
    def __init__ (self, conn=None):
        base_channel.__init__(self, conn)
        self.ac_in_buffer = ''
        self.ac_out_buffer = ''
        self.producer_fifo = asynchat.fifo()

    def handle_close(self):
        asynchat.async_chat.handle_close(self)

    def handle_read(self):
        asynchat.async_chat.handle_read(self)

    def handle_write(self):
        asynchat.async_chat.handle_write(self)

    def readable(self):
        return asynchat.async_chat.readable(self)

    def writable(self):
        return asynchat.async_chat.writable(self)

Abdullah Yoldas
 
On 3/15/06, Z. Kotzer <[EMAIL PROTECTED]> wrote:
I can not get error notifications when an asynchat based client tries to
connect to a non-responsive address.

To validate the problem I changed lib/test/test_asynchat.py as follows:


class echo_client(asynchat.async_chat):
   def __init__(self):
       asynchat.async_chat.__init__(self)
       self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
       self.connect(('10.135.0.2', PORT)) # <<<<<<<< Instead of HOST - set
an address that does not listen to this port
       self.set_terminator("\n")
       self.buffer = ""

   # <<<<<<<<<<<<<<< And added an error handler
   def handle_error(self):
       print 'ERROR'


Running it prints nothing - handle_error is not called and nothing is raised
from asyncore.loop().

Debugging it shows that asyncore.connect gets EWOULDBLOCK and returns
normally (as may be expected), select in asyncore.poll returns nothing
(including empty e) and the socket remains forever.

Anybody has an experience with this behaviour?

Thanks in advance!


--
http://mail.python.org/mailman/listinfo/python-list

-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to