17.6. asyncore — 异步套接字处理程序 — Python 文档
17.6. 异步 — 异步套接字处理程序
源代码: :source:`Lib/asyncore.py`
该模块提供了用于编写异步套接字服务客户端和服务器的基本基础结构。
只有两种方法可以让单个处理器上的程序“一次做不止一件事情”。 多线程编程是最简单和最流行的方法,但还有另一种非常不同的技术,它可以让您拥有多线程的几乎所有优点,而无需实际使用多线程。 只有当您的程序在很大程度上受 I/O 限制时,它才真正实用。 如果您的程序受处理器限制,那么抢占式调度线程可能是您真正需要的。 然而,网络服务器很少受处理器限制。
如果您的操作系统在其 I/O 库中支持 [X38X] 系统调用(并且几乎都支持),那么您可以使用它同时处理多个通信通道; 在您的 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 参数,如果为真,表示应该优先使用 poll() 而不是 select()(默认为False
) .map 参数是一个字典,其项目是要观看的频道。 当通道关闭时,它们会从它们的地图中删除。 如果省略 map,则使用全局映射。 频道(asyncore.dispatcher、asynchat.async_chat 及其子类的实例)可以在地图中自由混合。
- class asyncore.dispatcher
dispatcher 类是一个围绕低级套接字对象的瘦包装器。 为了使它更有用,它有一些从异步循环调用的事件处理方法。 否则,它可以被视为一个普通的非阻塞套接字对象。
在某些时间或在某些连接状态下触发低级事件告诉异步循环某些高级事件已经发生。 例如,如果我们要求一个套接字连接到另一台主机,当套接字第一次变为可写时,我们就知道已经建立了连接(此时您知道您可以期望成功写入它) )。 隐含的更高级别的事件是:
事件
描述
handle_connect()
由第一个读或写事件暗示
handle_close()
由没有可用数据的读取事件暗示
handle_accept()
由侦听套接字上的读取事件暗示
在异步处理过程中,每个映射通道的 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() 调用的新远程端点建立连接时,在侦听通道(被动开启器)上调用。
- readable()
每次围绕异步循环调用以确定是否应将通道的套接字添加到可以发生读取事件的列表中。 默认方法简单地返回
True
,表示默认情况下,所有通道都会对读取事件感兴趣。
- writable()
每次围绕异步循环调用以确定是否应将通道的套接字添加到可以发生写入事件的列表中。 默认方法简单地返回
True
,表示默认情况下,所有通道都会对写入事件感兴趣。
此外,每个通道都委托或扩展了许多套接字方法。 其中大多数与它们的套接字伙伴几乎相同。
- create_socket(family, type)
这与创建普通套接字相同,并且将使用相同的创建选项。 有关创建套接字的信息,请参阅 socket 文档。
- connect(address)
与普通套接字对象一样,address 是一个元组,其中第一个元素是主机要连接到的元素,第二个元素是端口号。
- send(data)
将 data 发送到套接字的远程端点。
- recv(buffer_size)
从套接字的远程端点最多读取 buffer_size 个字节。 空字符串表示通道已从另一端关闭。
请注意 recv() 可能会引发 socket.error 与 EAGAIN 或 EWOULDBLOCK,即使 select.select()[ X135X] 或 select.poll() 已报告套接字已准备好读取。
- listen(backlog)
侦听与套接字的连接。 backlog 参数指定排队连接的最大数量,并且应该至少为 1; 最大值取决于系统(通常为 5)。
- bind(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 接受一个文件描述符或文件对象以及一个可选的映射参数,并将其包装起来以与
poll()
或loop()
函数一起使用。 如果提供了一个文件对象或任何带有fileno()
方法的东西,该方法将被调用并传递给 file_wrapper 构造函数。 可用性:UNIX。
- class asyncore.file_wrapper
- file_wrapper 接受一个整数文件描述符并调用 os.dup() 来复制句柄,以便可以独立于 file_wrapper 关闭原始句柄。 此类实现了足够的方法来模拟供 file_dispatcher 类使用的套接字。 可用性:UNIX。
17.6.1. asyncore 示例基本 HTTP 客户端
这是一个非常基本的 HTTP 客户端,它使用 dispatcher 类来实现其套接字处理:
import asyncore, socket
class HTTPClient(asyncore.dispatcher):
def __init__(self, host, path):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect( (host, 80) )
self.buffer = 'GET %s HTTP/1.0\r\n\r\n' % path
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()
17.6.2. asyncore 示例基本回显服务器
这是一个基本的回显服务器,它使用 dispatcher 类来接受连接并将传入的连接分派给处理程序:
import asyncore
import socket
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(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 not None:
sock, addr = pair
print 'Incoming connection from %s' % repr(addr)
handler = EchoHandler(sock)
server = EchoServer('localhost', 8080)
asyncore.loop()