asyncore ---异步套接字处理程序

源代码: Lib/asyncore.py

3.6 版后已移除: 请使用 asyncio 相反。


注解

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

这个模块为编写异步套接字服务客户机和服务器提供了基本的基础结构。

只有两种方法可以让一个处理器上的程序“一次完成一件以上的事情”。多线程编程是最简单也是最流行的方法,但是还有另一种非常不同的技术,它可以让您几乎拥有多线程的所有优点,而不必实际使用多线程。只有当程序主要受I/O约束时,它才真正实用。如果您的程序是处理器绑定的,那么抢先调度的线程可能是您真正需要的。然而,网络服务器很少受处理器限制。

如果您的操作系统支持 select() 系统调用它的I/O库(几乎所有都是这样),然后您可以使用它同时处理多个通信通道;当您的I/O发生在“后台”时进行其他工作。尽管这个策略看起来很奇怪和复杂,特别是在一开始,它在许多方面比多线程编程更容易理解和控制。G. asyncore 模块为您解决了许多难题,使构建复杂的高性能网络服务器和客户机的任务变得轻而易举。对于“会话式”应用程序和协议, asynchat 模块是无价的。

两个模块背后的基本思想是创建一个或多个网络 渠道 ,类的实例 asyncore.dispatcherasynchat.async_chat . 创建通道将它们添加到全局映射中,由 loop() 如果你不提供你自己的功能 map .

创建初始通道后,调用 loop() 函数激活通道服务,直到最后一个通道(包括异步服务期间添加到映射中的任何通道)关闭为止。

asyncore.loop([timeout[, use_poll[, map[, 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 版更改: 家庭type 参数可以省略。

connect(address)

和普通的socket对象一样, 地址 是一个元组,其中第一个元素是要连接的主机,第二个元素是端口号。

send(data)

发送 data 到Socket的远程端点。

recv(buffer_size)

至多阅读 buffer_size 来自套接字远程端点的字节。空字节对象表示通道已从另一端关闭。

注意 recv() 可以提高 BlockingIOError 即使 select.select()select.poll() 已报告套接字已准备好读取。

listen(backlog)

监听与Socket的连接。这个 积压 参数指定排队连接的最大数量,并且应至少为1;最大值取决于系统(通常为5)。

bind(address)

将套接字绑定到 地址 . 套接字必须尚未绑定。(格式) 地址 取决于地址族---请参阅 socket 文档以获取更多信息。)将套接字标记为可重用(设置 SO_REUSEADDR 选项),调用 dispatcher 对象的 set_reuse_addr() 方法。

accept()

接受连接。套接字必须绑定到地址并侦听连接。返回值可以是 None 或一对 (conn, address) 在哪里? conn 是一个 new 可用于在连接上发送和接收数据的套接字对象,以及 地址 是绑定到连接另一端套接字的地址。什么时候? None 如果返回,则表示未发生连接,在这种情况下,服务器应忽略此事件并继续侦听进一步传入的连接。

close()

关闭Socket。以后对socket对象的所有操作都将失败。远程端点将不再接收数据(在刷新排队的数据之后)。当套接字被垃圾收集时,它们会自动关闭。

class asyncore.dispatcher_with_send

A dispatcher 子类,它添加了简单的缓冲输出功能,对简单的客户机很有用。更复杂的使用方法 asynchat.async_chat .

class asyncore.file_dispatcher

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

Availability UNIX。

class asyncore.file_wrapper

文件封装器接受整数文件描述符并调用 os.dup() 复制句柄,以便可以独立于文件封装器关闭原始句柄。此类实现了足够的方法来模拟套接字以供 file_dispatcher 类。

Availability UNIX。

异步示例基本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()

异步示例基本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()