# 18.6. `asyncore` — 异步套接字处理

源代码： Lib / asyncore.py

注意

此模块仅用于向后兼容。对于新代码，我们建议使用`asyncio`。

此模块提供用于编写​​异步套接字服务客户端和服务器的基本基础结构

在单个处理器上有一个程序只有两种方法可以“一次处理多个事件”。多线程编程是最简单和最流行的方法，但还有另一种非常不同的技术，具有几乎所有的多线程优势，而没有实际使用多线程。这真的只是实用的，如果你的程序大部分是I / O绑定。如果你的程序是处理器绑定，那么抢先调度的线程可能是你真正需要的。然而，网络服务器很少受处理器限制。

如果您的操作系统在其I / O库中支持`select()`系统调用（几乎所有操作系统调用），则可以使用它同时处理多个通信通道；在“背景”中执行I / O时执行其他工作。虽然此策略看起来很奇怪和复杂，特别是在开始时，它在许多方面比多线程编程更容易理解和控制。`asyncore`模块解决了许多困难的问题，使构建复杂的高性能网络服务器和客户端的任务变得轻而易举。对于“会话”应用程序和协议，伴随的`asynchat`模块是无价的。

两个模块的基本思想是创建一个或多个网络通道，类`asyncore.dispatcher`和`asynchat.async_chat`的实例。如果您没有提供自己的映射，则创建通道会将它们添加到由`loop()`函数使用的全局映射中。

一旦创建了初始通道，调用`loop()`函数就会激活通道服务，直到最后一个通道（包括在异步服务期间已经添加到映射的任何通道）关闭了。

`asyncore.``loop`([timeout[, use_poll[, map[, count]]]])

  输入轮询循环，在计数过程或所有打开的通道关闭后终止循环。所有参数都是可选的。count参数默认为None，导致只有当所有通道都关闭时，循环才终止。timeout参数设置适当的`select()`或`poll()`调用的超时参数，以秒为单位；默认值为30秒。use_poll参数如果为true，则表示应该优先于`select()`使用`poll()`（默认值为`False`

  map参数是一个字典，其项目是要观看的频道。随着频道关闭，它们将从地图中删除。如果省略map，则使用全局映射。通道（`asyncore.dispatcher`，`asynchat.async_chat`及其子类的实例）可以在地图中自由混合。

class `asyncore。` `分派器`

  `dispatcher`类是一个围绕低级套接字对象的薄包装。为了使它更有用，它有一些从异步循环调用的事件处理方法。否则，它可以被视为正常的非阻塞套接字对象。

  在某些时间或在某些连接状态中发射低级事件告诉异步循环，发生了某些更高级别的事件。例如，如果我们要求一个套接字连接到另一个主机，我们知道在套接字第一次可写时已经建立了连接（在这一点上，您知道您可以用成功的期望写入它） ）。隐含的更高级别事件是：

  事件 | 描述
  --- | ---
  `handle_connect()` | 由第一个读或写事件隐含
  `handle_close()` | 由没有可用数据的读取事件引起
  `handle_accepted()` | 通过侦听套接字上的读取事件隐含

  在异步处理期间，每个映射通道的`readable()`和`writable()`方法用于确定通道的套接字是否应添加到通道列表`select()` ed或`poll()` ed读取和写入事件。

  因此，该组通道事件大于基本套接字事件。可以在您的子类中覆盖的完整方法集如下：

  `handle_read`()

    当异步循环检测到通道的套接字上的`read()`调用将成功时调用。

  `handle_write`()

    当异步循环检测到可以写入可写套接字时调用。通常这种方法将实现必要的性能缓冲。例如：

        def handle_write(self):
            sent = self.send(self.buffer)
            self.buffer = self.buffer[sent:]

  `handle_expt`()

    当有套接字连接的带外（OOB）数据时调用。这几乎不会发生，因为OOB被精心支持，很少使用。

  `handle_connect`()

    当活动开启者的套接字实际建立连接时调用。可能发送“欢迎”横幅，或者例如与远程端点发起协议协商。

  `handle_close`()

    在套接字关闭时调用。

  `handle_error`()

    当引发异常并且未以其他方式处理时调用。默认版本打印精简跟踪。

  `handle_accept`()

    当可以与为本地端点发出`connect()`调用的新远程端点建立连接时，在侦听通道（被动打开程序）上调用。在版本3.2中已弃用；请改用`handle_accepted()`。

    自3.2版起已弃用。

  `handle_accepted`(sock, addr)

    当与为本地端点发出`connect()`调用的新远程端点建立连接时，在侦听通道（被动打开程序）上调用。sock是可用于在连接上发送和接收数据的新套接字对象，addr是绑定到另一端套接字的地址的连接。

    版本3.2中的新功能。

  `readable`()

    每次在异步循环时调用以确定是否应将通道的套接字添加到可能发生读事件的列表中。默认方法只返回`True`，表示默认情况下所有通道都对读事件感兴趣。

  `writable`()

    每次在异步循环时调用以确定是否应将通道的套接字添加到可能发生写事件的列表中。默认方法只返回`True`，表示默认情况下所有通道都对写事件感兴趣。

  此外，每个通道都委派或扩展了许多套接字方法。其中大多数与其套接伙伴几乎相同。

  `create_socket`(family=socket.AF_INET, type=socket.SOCK_STREAM)

    这与创建正常的套接字相同，并且将使用相同的选项进行创建。有关创建套接字的信息，请参阅`socket`文档。

    在版本3.3中更改： 系列和类型参数可以省略。

  `connect`(address)

    与正常的套接字对象一样，地址是主机连接到的第一个元素的元组，第二个是端口号。

  `send`(data)

    将数据发送到套接字的远程端点。

  `recv`(buffer_size)

    最多从套接字的远程端点读取buffer_size字节。空字节对象意味着通道已从另一端关闭。

    请注意，即使`select.select()`或`select.poll()`，`recv()`可能引发`BlockingIOError`已报告套接字准备好读取。

  `listen`(backlog)

    监听与套接字的连接。backlog参数指定排队连接的最大数量，且应至少为1；最大值是系统相关的（通常为5）。

  `bind`(address)

    将套接字绑定到地址。套接字不必已绑定。（地址的格式取决于地址系列 - 有关详细信息，请参阅`socket`文档）。要将套接字标记为可重用（设置`SO_REUSEADDR`选项），请调用`dispatcher`对象的`set_reuse_addr()`方法。

  `accept`()

    接受连接。套接字必须绑定到地址并侦听连接。返回值可以是`None`或对`（conn， 地址）`，其中conn 是可用于在连接上发送和接收数据的新套接字对象，address是连接另一端的套接字的地址。当返回`None`时，表示连接未发生，在这种情况下，服务器应忽略此事件并继续侦听其他传入连接。

  `close`()

    关闭套接字。对套接字对象的所有未来操作都将失败。远程端点将不再接收数据（在排队的数据被刷新之后）。套接字在被垃圾回收时自动关闭。

class `asyncore.``dispatcher_with_send`

  `dispatcher`子类，它增加了简单的缓冲输出功能，对简单的客户端非常有用。对于更复杂的用法，请使用`asynchat.async_chat`。

class `asyncore.``file_dispatcher`

  file_dispatcher接受一个文件描述器或file object以及一个可选的map参数，并将其封装用于`poll()`或`loop()`如果提供了文件对象或任何具有`fileno()`方法的方法，那么该方法将被调用并传递给`file_wrapper`构造函数。可用性：UNIX。

class `asyncore.``file_wrapper`

  file_wrapper接受整数文件描述器并调用`os.dup()`复制句柄，以便原始句柄可以独立于file_wrapper而关闭。此类实现足够的方法来模拟由`file_dispatcher`类使用的套接字。可用性：UNIX。

## 18.6.1. asyncore基本的HTTP客户端示例

这里是一个非常基本的HTTP客户端，它使用`dispatcher`类来实现其套接字处理：

    import asyncore

    class HTTPClient(asyncore.dispatcher):

        def __init__(self, host, path):
            asyncore.dispatcher.__init__(self)
            self.create_socket()
            self.connect( (host, 80) )
            self.buffer = bytes('GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' %
                                (path, host), 'ascii')

        def handle_connect(self):
            pass

        def handle_close(self):
            self.close()

        def handle_read(self):
            print(self.recv(8192))

        def writable(self):
            return (len(self.buffer) > 0)

        def handle_write(self):
            sent = self.send(self.buffer)
            self.buffer = self.buffer[sent:]

    client = HTTPClient('www.python.org', '/')
    asyncore.loop()

## 18.6.2. asyncore示例，基本echo服务器

这里是一个基本的回显服务器，它使用`dispatcher`类接受连接并将传入连接分派给处理程序：

    import asyncore

    class EchoHandler(asyncore.dispatcher_with_send):

        def handle_read(self):
            data = self.recv(8192)
            if data:
                self.send(data)

    class EchoServer(asyncore.dispatcher):

        def __init__(self, host, port):
            asyncore.dispatcher.__init__(self)
            self.create_socket()
            self.set_reuse_addr()
            self.bind((host, port))
            self.listen(5)

        def handle_accepted(self, sock, addr):
            print('Incoming connection from %s' % repr(addr))
            handler = EchoHandler(sock)

    server = EchoServer('localhost', 8080)
    asyncore.loop()