Skip to main content

18.6. asyncore —异步套接字处理程序

源代码: Lib/asyncore.py

3.6 版后已移除: 请使用 asyncio


注解

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

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

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

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

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

一旦创建了初始频道,调用 loop() 功能激活频道服务,其继续直到关闭最后一个频道(包括在异步服务期间已经被添加到映射的任何频道)。

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

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

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

class asyncore.dispatcher

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 是可用于在连接上发送和接收数据的 new 套接字对象,addr 是绑定到连接另一端的套接字的地址。

3.2 新版功能.

readable()

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

writable()

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

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

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

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

在 3.3 版更改: familytype 参数可以省略。

connect(address)

与正常的套接字对象一样,address 是一个元组,具有主机要连接的第一个元素,第二个是端口号。

send(data)

data 发送到套接字的远程端点。

recv(buffer_size)

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

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

listen(backlog)

监听与插座的连接。 backlog 参数指定排队连接的最大数量,且应至少为1;最大值是系统相关的(通常为5)。

bind(address)

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

accept()

接受连接。套接字必须绑定到地址并侦听连接。返回值可以是 None 或一对 (conn, address),其中 conn 是可用于在连接上发送和接收数据的 new 套接字对象,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服务器

这里是一个基本的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()