[issue12498] asyncore.dispatcher_with_send, disconnection problem + miss-conception

Charles-François Natali report at bugs.python.org
Sun Oct 30 12:16:24 CET 2011


Charles-François Natali <neologix at free.fr> added the comment:

> This does not seem to work.

Yes, the proposed fix is buggy: the solution is to drain the output
buffer, guarding against recursive calls between send() and
handle_close() (patch attached).

> Note that after the first 'handle_close' has been printed, there are
> no 'handle_write' printed, which indicates that the operating system
> (here linux) states that the socket is not writable.

That's because you're closing the socket, whereas the original report
is dealing with half-shutdown connections.
Try this:
"""
 class Reader(asyncore.dispatcher):
     def __init__(self, sock):
         asyncore.dispatcher.__init__(self, sock)
+        self.shutdown = False

     def writable(self):
         return False

     def handle_read(self):
         self.recv(64)
-        self.close()
+        if not self.shutdown:
+            self.shutdown = True
+            self.socket.shutdown(socket.SHUT_WR)

 class Writer(asyncore.dispatcher_with_send):
     def __init__(self, address):
"""

This will behave as expected.
Calling socket.shutdown(socket.SHUT_WR) means that you won't send any
more data, but that you're still ready to receive: it results in a FIN
being sent to the remote endpoint, which will receive EOF on recv().
But subsequent send() from the remote endpoint will still succeed.
Whereas with socket.close(), you not only shut down your sending part,
but you also signify that you don't want to receive data any more: a
FIN will be sent immediately to the remote endpoint, and on subsequent
send() from the remote endpoint, a RST will be returned, which will
result in EPIPE (or, in there was data in the receive socket buffer
when the local endpoint was closed, a RST will be sent directly,
resulting in ECONNRESET).

I still need to write a test.

----------
keywords: +patch
Added file: http://bugs.python.org/file23552/asyncore_drain.diff

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue12498>
_______________________________________
-------------- next part --------------
diff --git a/Lib/asyncore.py b/Lib/asyncore.py
--- a/Lib/asyncore.py
+++ b/Lib/asyncore.py
@@ -535,6 +535,7 @@
         num_sent = 0
         num_sent = dispatcher.send(self, self.out_buffer[:512])
         self.out_buffer = self.out_buffer[num_sent:]
+        return num_sent
 
     def handle_write(self):
         self.initiate_send()
@@ -548,6 +549,15 @@
         self.out_buffer = self.out_buffer + data
         self.initiate_send()
 
+    def handle_close(self):
+        if not self.closing:
+            self.closing = True
+            # try to drain the output buffer
+            while self.writable() and self.initiate_send() > 0:
+                pass
+        self.close()
+
+
 # ---------------------------------------------------------------------------
 # used for debugging.
 # ---------------------------------------------------------------------------


More information about the Python-bugs-list mailing list