asyncore —异步套接字处理程序

源代码: Lib/asyncore.py

自 3.6 版起已弃用:请改用asyncio


Note

存在此模块仅是为了向后兼容。对于新代码,我们建议使用asyncio

该模块提供了用于编写异步套接字服务 Client 端和服务器的基本基础结构。

只有一种方法可以使程序在单个处理器上“一次完成一个以上的事情”。多线程编程是最简单,最流行的方法,但是还有另一种非常不同的技术,它使您几乎拥有多线程的所有优点,而无需实际使用多个线程。仅当您的程序很大程度上受 I/O 约束时,这才是切实可行的。如果您的程序受处理器限制,那么抢占式 sched 线程可能正是您 true 需要的。但是,Web Service 器很少受处理器限制。

如果您的 os 在其 I/O 库中支持select()系统调用(几乎所有方法都支持),则可以使用它同时处理多个通信通道。在“ I/O”发生在“后台”时进行其他工作。尽管这种策略看似奇怪且复杂,尤其是乍一看,但它在很多方面都比多线程编程更易于理解和控制。 asyncore模块为您解决了许多难题,使构建复杂的高性能 Web Service 器和 Client 端的任务变得很轻松。对于“会话式”应用程序和协议,配套的asynchat模块是无价的。

这两个模块的基本思想是创建一个或多个网络* channel ,它们是类asyncore.dispatcherasynchat.async_chat的实例。创建通道会将它们添加到全局 Map 中,如果您未为其提供自己的 map *,则由loop()函数使用。

创建初始通道后,调用loop()函数将激活通道服务,该操作将一直持续到最后一个通道(包括异步服务期间已添加到 Map 的任何通道)关闭为止。

  • asyncore. loop([* timeout * [,* use_poll * [,* map * [,* count *]]]])
    • Importing 一个轮询循环,该循环在计数pass或关闭所有打开的通道后终止。所有参数都是可选的。 * count *参数默认为None,导致循环仅在所有通道均已关闭时终止。 * timeout *参数设置相应的select()poll()调用的超时参数,以秒为单位;默认值为 30 秒。 * use_poll *参数(如果为 true)表示应优先使用poll()而不是select()(默认为False)。
  • map 参数是一个词典,其项是要观看的 Channels。通道关闭后,会将其从 Map 中删除。如果Ellipsis map *,则使用全局 Map。通道(asyncore.dispatcherasynchat.async_chat的实例及其子类)可以在 Map 中自由混合。
  • 类别 asyncore. dispatcher
    • dispatcher类是底层套接字对象的瘦包装。为了使其更有用,它具有一些用于事件处理的方法,这些方法从异步循环中调用。否则,可以将其视为普通的非阻塞套接字对象。

在某些时间或在某些连接状态下触发低级事件会告诉异步循环某些高级事件已经发生。例如,如果我们要求套接字将其连接到另一台主机,则我们知道该套接字在第一次可写时就已经构建了连接(此时,您知道可以期望成功写信给它)。隐含的更高级别的事件是:

EventDescription
handle_connect()由第一个读取或写入事件暗示
handle_close()读事件所隐含,无可用数据
handle_accepted()由侦听套接字上的读取事件暗示

在异步处理期间,每个 Map 的通道的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 是一个 new 套接字对象,可用于在连接上发送和接收数据,而 addr *是绑定到连接另一端的套接字的地址。

3.2 版中的新Function。

  • readable ( )

    • 每次围绕异步循环调用一次,以确定是否将通道的套接字添加到可能发生读取事件的列表中。默认方法仅返回True,指示默认情况下,所有通道都将对读取事件感兴趣。
  • writable ( )

    • 每次围绕异步循环调用一次,以确定是否将通道的套接字添加到可能发生写事件的列表中。默认方法仅返回True,指示默认情况下,所有通道都将对写入事件感兴趣。

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

  • create_socket(* family = socket.AF_INET type = socket.SOCK_STREAM *)
    • 这与普通套接字的创建相同,并且将使用相同的选项进行创建。有关创建套接字的信息,请参考socket文档。

在版本 3.3 中更改:* family type *参数可以Ellipsis。

  • connect(地址)

    • 与普通套接字对象一样,* address *是一个 Tuples,其中第一个元素是主机要连接的元素,第二个是端口号。
  • send(* data *)

    • 将* data *发送到套接字的远程端点。
  • recv(* buffer_size *)

    • 从套接字的远程端点读取最多* buffer_size *个字节。空字节对象表示该通道已从另一端关闭。

请注意,即使select.select()select.poll()已报告套接字已准备好读取,但recv()可能会引发BlockingIOError

  • listen(积压)

    • 侦听与套接字的连接。 * backlog *参数指定排队连接的最大数量,并且至少应为 1;最大值取决于系统(通常为 5)。
  • bind(地址)

    • 将套接字绑定到* address 。套接字必须尚未绑定。 ( address *的格式取决于地址系列-有关更多信息,请参见socket文档。)要将套接字标记为可重用(设置SO_REUSEADDR选项),请调用dispatcher对象的set_reuse_addr()方法。
  • accept ( )

    • 接受连接。套接字必须绑定到一个地址并监听连接。返回值可以是None或对(conn, address),其中* conn 是可用于在连接上发送和接收数据的 new 套接字对象, address *是在连接另一端绑定到套接字的地址。当返回None时,表示未构建连接,在这种情况下,服务器应仅忽略此事件并 continue 侦听其他传入的连接。
  • close ( )

    • 关闭 socket。套接字对象上所有以后的操作都将失败。远程端点将不再接收任何数据(在清除排队的数据之后)。套接字在被垃圾回收时会自动关闭。
  • 类别 asyncore. dispatcher_with_send

    • dispatcher子类,它添加了简单的缓冲输出Function,对简单的 Client 端很有用。对于更复杂的用法,请使用asynchat.async_chat
  • 类别 asyncore. file_dispatcher

    • file_dispatcher 使用文件 Descriptors 或file object以及可选的 map 参数,并将其包装以供poll()loop()函数使用。如果为文件对象或fileno()方法提供了任何东西,该方法将被调用并传递给file_wrapper构造函数。

Availability: Unix.

  • 类别 asyncore. file_wrapper
    • file_wrapper 使用整数文件 Descriptors,并调用os.dup()复制该句柄,以便可以独立于 file_wrapper 关闭原始句柄。此类实现了足够的方法来模拟供file_dispatcher类使用的套接字。

Availability: Unix.

asyncore 基本 HTTPClient 端示例

这是一个非常基本的 HTTPClient 端,它使用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()

asyncore 基本回显服务器示例

这是一个基本的回显服务器,该服务器使用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()