diff --git a/Doc/library/asyncore.rst b/Doc/library/asyncore.rst index 8cdfe200bfe..7edf6128f70 100644 --- a/Doc/library/asyncore.rst +++ b/Doc/library/asyncore.rst @@ -209,10 +209,13 @@ any that have been added to the map during asynchronous service) is closed. .. method:: accept() Accept a connection. The socket must be bound to an address and listening - for connections. The return value is a pair ``(conn, address)`` where - *conn* is a *new* socket object usable to send and receive data on the - connection, and *address* is the address bound to the socket on the other - end of the connection. + for connections. The return value can be either ``None`` or a pair + ``(conn, address)`` where *conn* is a *new* socket object usable to send + and receive data on the connection, and *address* is the address bound to + the socket on the other end of the connection. + When ``None`` is returned it means the connection didn't take place, in + which case the server should just ignore this event and keep listening + for further incoming connections. .. method:: close() @@ -222,6 +225,12 @@ any that have been added to the map during asynchronous service) is closed. flushed). Sockets are automatically closed when they are garbage-collected. +.. class:: dispatcher_with_send() + + A :class:`dispatcher` subclass which adds simple buffered output capability, + useful for simple clients. For more sophisticated usage use + :class:`asynchat.async_chat`. + .. class:: file_dispatcher() A file_dispatcher takes a file descriptor or :term:`file object` along @@ -238,7 +247,7 @@ any that have been added to the map during asynchronous service) is closed. socket for use by the :class:`file_dispatcher` class. Availability: UNIX. -.. _asyncore-example: +.. _asyncore-example-1: asyncore Example basic HTTP client ---------------------------------- @@ -248,7 +257,7 @@ implement its socket handling:: import asyncore, socket - class http_client(asyncore.dispatcher): + class HTTPClient(asyncore.dispatcher): def __init__(self, host, path): asyncore.dispatcher.__init__(self) @@ -272,6 +281,45 @@ implement its socket handling:: sent = self.send(self.buffer) self.buffer = self.buffer[sent:] - c = http_client('www.python.org', '/') - asyncore.loop() + client = HTTPClient('www.python.org', '/') + asyncore.loop() + +.. _asyncore-example-2: + +asyncore Example basic echo server +---------------------------------- + +Here is abasic echo server that uses the :class:`dispatcher` class to accept +connections and dispatches the incoming connections to a handler:: + + import asyncore + import socket + + class EchoHandler(asyncore.dispatcher_with_send): + + def handle_read(self): + data = self.recv(8192) + self.send(data) + + class EchoServer(asyncore.dispatcher): + + def __init__(self, host, port): + asyncore.dispatcher.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + self.bind((host, port)) + self.listen(5) + + def handle_accept(self): + pair = self.accept() + if pair is None: + return + else: + sock, addr = pair + print('Incoming connection from %s' % repr(addr)) + handler = EchoHandler(sock) + + server = EchoServer('localhost', 8080) + asyncore.loop() + diff --git a/Lib/asyncore.py b/Lib/asyncore.py index 7f06e43e716..861495ddfce 100644 --- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -346,12 +346,15 @@ class dispatcher: # XXX can return either an address pair or None try: conn, addr = self.socket.accept() - return conn, addr + except TypeError: + return None except socket.error as why: - if why.args[0] == EWOULDBLOCK: - pass + if why.args[0] in (EWOULDBLOCK, ECONNABORTED): + return None else: raise + else: + return conn, addr def send(self, data): try: diff --git a/Lib/smtpd.py b/Lib/smtpd.py index 57ad0898419..ab3e0e209b0 100755 --- a/Lib/smtpd.py +++ b/Lib/smtpd.py @@ -297,22 +297,11 @@ class SMTPServer(asyncore.dispatcher): localaddr, remoteaddr), file=DEBUGSTREAM) def handle_accept(self): - try: - conn, addr = self.accept() - except TypeError: - # sometimes accept() might return None - return - except socket.error as err: - # ECONNABORTED might be thrown - if err.args[0] != errno.ECONNABORTED: - raise - return - else: - # sometimes addr == None instead of (ip, port) - if addr == None: - return - print('Incoming connection from %s' % repr(addr), file=DEBUGSTREAM) - channel = SMTPChannel(self, conn, addr) + pair = self.accept() + if pair is not None: + conn, addr = pair + print('Incoming connection from %s' % repr(addr), file=DEBUGSTREAM) + channel = SMTPChannel(self, conn, addr) # API for "doing something useful with the message" def process_message(self, peer, mailfrom, rcpttos, data): diff --git a/Misc/NEWS b/Misc/NEWS index b7336256e40..d690318652f 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -143,6 +143,9 @@ C-API Library ------- +- Issue 6706: asyncore accept() method no longer raises EWOULDBLOCK/ECONNABORTED + on incomplete connection attempt but returns None instead. + - Issue #10266: uu.decode didn't close in_file explicitly when it was given as a filename. Patch by Brian Brazil.