多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## **1. 事件循环基类** 事件循环是由[asyncio]()提供的核心执行装置。它提供了多种服务,包括: + 注册、执行和关闭延时调用(超时) + 为各种通信创建客户端和服务端[传输]() + 为一个外部程序通信启动子进程和相关的[传输]() + 把高成本的函数调用委托到线程池 #### *class* asyncio.**BaseEventLoop** &emsp;&emsp; 此类是一个实现细节。此类是[AbstractEventLoop]()的子类,是你在[asyncio]()中找到的具体事件循环类的基类。它不应该被直接使用,而是使用[AbstractEventLoop]()来代替它。`BaseEventLoop`不应该被第三方代码继承,它的内部接口并不稳定。 #### *class* asyncio.**AbstractEventLoop** &emsp;&emsp; 事件循环抽象基类。 &emsp;&emsp; 这个类不是[线程安全]()的。 ### **1.1. 运行事件循环** #### AbstractEventLoop.**run\_forever**() &emsp;&emsp; 一直运行直到[stop()]()被调用。如果[stop()]()在run\_forever()之前被调用,它将轮询一次I/O selector(timeout为0),然后运行所有响应I/O事件的回调(以及那些已经被安排的回调),然后退出。如果[stop()]()在run\_forever()运行时被调用,它将运行当前的一批回调然后退出。请注意,那些被回调函数安排的回调不会在这种情况下运行;它们将会在下一次run\_forever被调用时运行。 &emsp;&emsp; *改动于3.5.1版本。* #### AbstractEventLoop.**run\_until\_complete**(future) &emsp;&emsp; 一直运行直到future运行完成。 &emsp;&emsp; 如果参数是一个[协程对象](),它将被[ensure\_future()]()打包。 &emsp;&emsp; 返回Future的结果,或者引发它的异常。 #### AbstractEventLoop.**stop**() &emsp;&emsp; 停止运行事件循环。 &emsp;&emsp; 它将在一个适当的时机使得run\_forever()退出。 &emsp;&emsp; *改动于3.5.1版本。* #### AbstractEventLoop.**is\_closed**() &emsp;&emsp; 当事件循环关闭时返回`True`。 &emsp;&emsp; *新增于3.4.2版本。* #### AbstractEventLoop.**close**() &emsp;&emsp; 关闭事件循环。这个事件循环必须没有在运行。挂起的回调将会丢失。 &emsp;&emsp; 此方法清空队列、关闭执行器,且不会等待执行器运行结束。 &emsp;&emsp; 此方法是幂等且不可逆的。在此方法调用之后不应该调用任何其他方法。 ### **1.2. 调用** 大多数[asyncio]()函数不接受参数。如果你想给你的回调函数一些参数,使用functools.partial()。例如,`loop.call_soon(functools.partial(print, "Hello", flush=True))`将会调用`print("Hello", flush=True)`。 > 注意:`functools.partial()`比`lambda`方法更好,因为`asyncio`能检查`functools.partial()`对象,使得能在调试模式下展示参数。相比之下使用`lambda`会有糟糕的表现。 #### AbstractEventLoop.**call_soon**(callback, \*args) &emsp;&emsp; 安排尽快调用这个callback(回调)。这个callback会在call_soon()返回、当控制权返回事件循环时被调用。 &emsp;&emsp; 这个操作是一个先入先出队列,多个call_soon()注册了多个callback会按照它们的顺序依次被调用。每个callback都只会被调用一次。 &emsp;&emsp; callback后面的所有参数会在callback被调用时传递给callback。 &emsp;&emsp; 返回值是[asyncio.Handle]()的一个实例,用于中止这个callback。 &emsp;&emsp; (可以使用`functools.partial()`来给callback传递参数) #### AbstractEventLoop.**call\_soon\_threadsafe**(callback, \*args) &emsp;&emsp; 类似call_soon(),但是是线程安全的。 &emsp;&emsp; 可以参考[使用asyncio开发]()的 并发和多线程 段落。 ### **1.3. 延时调用** 事件循环有自己的内部时钟用于计算超时。这个时钟依赖于(特定平台的)事件循环来执行;理想情况下它是一个单调时钟(monotonic clock)。通常它是一个不同于time.time()的时钟。 > 注意:超时(相对延时或绝对时刻)不应该超过一天。 #### AbstractEventLoop.**call_later**(delay, callback, \*args) &emsp;&emsp; 安排一个callback在delay秒之后被调用(delay可以是int和float)。 &emsp;&emsp; 返回值是[asyncio.Handle]()的一个实例,用于中止这个callback。 &emsp;&emsp; 每次调用call_later()都有callback将被调用一次。如果多个回调被安排在同一时刻调用,谁先被调用是不确定的。 &emsp;&emsp; 可选参数args将被传递给callback作为调用时的参数。如果你想给callback传递带名字的参数,把它们包起来或者用`functools.partial()`。 #### AbstractEventLoop.**call\_at**(when, callback, \*args) &emsp;&emsp; 安排callback在给定的时间戳when(int或者float)时调用;用AbstractEventLoop.time()作为参考。 &emsp;&emsp; 这个方法的行为和AbstractEventLoop.call\_later()一样。 &emsp;&emsp; 返回值是[asyncio.Handle]()的一个实例,用于中止这个callback。 #### AbstractEventLoop.**time**() &emsp;&emsp; 返回当前时间,一个float值,根据事件循环的内部时钟。 ### **1.4. Futures** #### AbstractEventLoop.**create_future**() &emsp;&emsp; 创建一个关联到这个事件循环的[asyncio.Future]()对象。 &emsp;&emsp; 这是asyncio中创建Future的首选方式,作为事件循环的实现可以提供Future类的代替实现(有更好的性能或表现)。 &emsp;&emsp; *新增于3.5.2版本。* ### **1.5. 任务** #### AbstractEventLoop.**create\_task**(coro) &emsp;&emsp; 安排一个[协程对象]()的执行:把它包装在一个future里。返回一个[任务(Task)]()对象。 &emsp;&emsp; 第三方事件循环可以使用他们自己的Task子类来交互。在这种情况下,返回值类型是Task的子类。 &emsp;&emsp; 这个方法是Python 3.4.2版本加入的。使用[async()]()可以支持旧的Python版本。 &emsp;&emsp; *新增于3.4.2版本。* #### AbstractEventLoop.**set\_task\_factory**(factory) &emsp;&emsp; 为AbstractEventLoop.create_task()设置一个task的工厂。 &emsp;&emsp; 如果factory参数是None,将会设置为默认工厂。 &emsp;&emsp; 如果factory参数是*可被调用*(callable)的,他应该可以接受(loop, coro)作为参数,loop是一个可用的事件循环,coro是协程对象。它必须返回一个[asyncio.Future]()的兼容对象。 &emsp;&emsp; *新增于3.4.4版本。* #### AbstractEventLoop.**get\_task\_factory**() &emsp;&emsp; 返回任务工厂,如果用的是默认的工厂,返回None。 &emsp;&emsp; *新增于3.4.4版本。* ### **1.6. 创建连接** #### *coroutine* AbstractEventLoop.**create\_connection**(protocol_factory, host=None, port=None, \*, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None) &emsp;&emsp; 创建一个连接到给定的host(主机)和port(端口)的流传输连接:socket family是AF_INET还是AF_INET6取决于host(或者指定了family参数),socket type是SOCK_STREAM。protocol_factory必须是*可被调用*(callable)的且返回一个[传输(protocol)]()实例。 &emsp;&emsp; 这是一个协程方法,它将在后台默默地建立一个连接。建立成功的时候,返回一个`(transport, protocol)`元组((传输,协议)对)。 &emsp;&emsp; 按时间顺序的大概底层操作如下: 1. 连接建立,创建一个传输(transport)来代表它。 2. protocol_factory被调用(没有参数),返回一个协议(protocol)实例。 3. 这个协议(protocol)实例和传输(transport)捆绑在一起,其connection_made()方法被调用。 4. 这个协程成功地返回(transport, protocol)对。 &emsp;&emsp; 创建的传输是一个依赖于实现的双向流。 > 注意:protocol_factory可以是任意的*可被调用*(callable)对象,不一定要是一个类。例如,你想使用一个预先创建的协议实例,你可以写`lambda: my_protocol`。 &emsp;&emsp; 这些选项可以改变连接创建方式: + ssl:如果给了这个参数且不为false,将会创建一个SSL/TLS传输(默认是一个普通的TCP传输被创建)。如果ssl是一个ssl.SSLContext对象,这个上下文将被用于创建传输;如果ssl是True,一个带有一些未被指明的默认设置的上下文将被使用。(可以去参考官方文档ssl章节的SSL/TLS security considerations段落) + server_hostname,只能和ssl参数一起使用,用于设置或覆盖将要匹配目标主机证书的主机名。默认会使用host参数。如果host参数为空,没有默认值,你必须填写server_hostname参数的值。如果server_hostname是一个空字符串,主机名匹配就被禁用了(这非常不安全,可能会产生中间人攻击)。 + family,proto,flags是可选的地址族(address family)、协议(protocol)、位掩码(flags),来传给getaddrinfo()进行host参数的域名解析。如果给定这些参数,这些应该都是相应的socket模块的常量整数。 + sock,如果给定这个参数,它必须是已存在的、已连接的socket.socket对象,来用于传输。如果给定了这个参数,host、port、family、proto、flags和local_addr参数都不应该给定。 + local_addr,如果给定了这个参数,它应该是一个来用绑定到本地socket的(local_host, local_port)元组((本地地址,本地端口)元组)。local_host和local_port将使用getaddrinfo()来查询,类似于host和port参数。 *改动于3.5版本*:SSL/TLS现在支持Windows上的[ProactorEventLoop]()了。 > 还可以看看[open_connection()]()函数,它可以获得(StreamReader, StreamWriter)对,而不是一个协议。 #### *coroutine* AbstractEventLoop.**create_datagram_endpoint**(protocol_factory, local_addr=None, remote_addr=None, \*, family=0, proto=0, flags=0, reuse_address=None, reuse_port=None, allow_broadcast=None, sock=None) &emsp;&emsp; 创建一个数据报连接:socket family是AF_INET还是AF_INET6取决于host(或者指定了family参数),socket type是SOCK_DGRAM。protocol_factory必须是*可被调用*(callable)的且返回一个[传输(protocol)]()实例。 &emsp;&emsp; 这是一个协程方法,它将在后台默默地建立一个连接。建立成功的时候,返回一个`(transport, protocol)`元组((传输,协议)对)。 &emsp;&emsp; 这些选项可以改变连接创建方式: + local_addr,如果给定了这个参数,它应该是一个来用绑定到本地socket的(local_host, local_port)元组((本地地址,本地端口)元组)。local_host和local_port将使用getaddrinfo()来查询。 + remote_addr,如果给定了这个参数,它应该是一个来用绑定到远程地址的socket的(remote_host, remote_port)元组((本地地址,本地端口)元组)。remote_host和remote_port将使用getaddrinfo()来查询。 + family,proto,flags是可选的地址族(address family)、协议(protocol)、位掩码(flags),来传给getaddrinfo()进行host参数的域名解析。如果给定这些参数,这些应该都是相应的socket模块的常量整数。 + reuse_address,这个参数告诉内核在TIME_WAIT状态下重用本地的socket,而不是等待它自然的超时过期。如果没有指定这个参数,在UNIX下默认设为True。 + reuse_port,这个参数告诉内核允许这个端点绑定到这个端口,即使有另外的已存在的端点绑定了这个端口,只要它们在创建时也设置了这个参数就行。这个选项在Windows和部分UNIX上不被支持。如果SO_REUSEPORT这个常量没有被定义,那这个能力就是不支持的。 + allow_broadcast,这个参数告诉内核允许这个端点发送消息给广播地址。 + sock参数可以被指定来使用一个先前存在的、已连接的socket.socket对象来用于传输。如果指定了这个参数,local_addr和remote_addr必须被省略(必须为None)。 &emsp;&emsp; 在Windows上的[ProactorEventLoop]()循环里,这个方法还不被支持。 &emsp;&emsp; 可以参考[UDP客户端协议]()和[UDP服务端协议]()实例。 #### *coroutine* AbstractEventLoop.**create_unix_connection**(protocol_factory, path, \*, ssl=None, sock=None, server_hostname=None) &emsp;&emsp; 创建一个UNIX连接:socket family是AF_UNIX,socket type是SOCK_STREAM。AF_UNIX是用来进行同一机器上的不同进程间的高效通信的。 &emsp;&emsp; 这是一个协程方法,它将在后台默默地建立一个连接。建立成功的时候,返回一个`(transport, protocol)`元组((传输,协议)对)。 &emsp;&emsp; 参数可以参考AbstractEventLoop.create_connection()方法的参数。 &emsp;&emsp; 仅适用于:UNIX。 ### **1.7. 创建监听连接** #### *coroutine* AbstractEventLoop.**create_server**(protocol_factory, host=None, port=None, \*, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None) &emsp;&emsp; 创建一个TCP服务端(socket type是SOCK_STREAM),绑定到host和port参数(主机和端口)。 &emsp;&emsp; 返回一个[Server]()对象,它的sockets属性包含了已创建的socket。使用[Server.close()]()方法来停止这个服务端:关闭监听的sockets。 &emsp;&emsp; 参数: + host参数可以是一个字符串,在这种情况下TCP服务端绑定到host和port参数。host参数也可以是字符串序列,这种情况下TCP服务端绑定到host序列里的所有主机。如果host是空字符串或者None,所有的接口都被假定,返回一个有多个socket的list(最可能的用于IPv4的一个和IPv6的一个)。 + family参数可以设置为socket.AF_INET或者AF_INET6来使socket强制使用IPv4或者IPv6。如果没有设置就会取决于host(默认是socket.AF_UNSPEC)。 + flags是用于的getaddrinfo()的位掩码。 + sock参数可以被指定,用来使用先前存在的socket对象。如果指定了,host和port参数应该被省略(必须为None)。 + backlog参数是传递给[listen()]()方法的最大等待连接数(默认为100)。 + ssl参数可以设置一个SSLContext对象来在一个接受的连接上启用SSL。 + reuse_address,这个参数告诉内核在TIME_WAIT状态下重用本地的socket,而不是等待它自然的超时过期。如果没有指定这个参数,在UNIX下默认设为True。 + reuse_port,这个参数告诉内核允许这个端点绑定到这个端口,即使有另外的已存在的端点绑定了这个端口,只要它们在创建时也设置了这个参数就行。这个选项在Windows上不被支持。 &emsp;&emsp; 这个是一个协程方法。 &emsp;&emsp; *改动于3.5版本*:SSL/TLS现在支持Windows上的[ProactorEventLoop]()了。 > 还可以看看[start_server()]()函数,它创建一个(StreamReader, StreamWriter)对,并且回调一个方法。 &emsp;&emsp; *改动于3.5.1版本*:host参数现在可以是一个字符串序列。 #### *coroutine* AbstractEventLoop.**create_unix_server**(protocol_factory, path=None, \*, sock=None, backlog=100, ssl=None) &emsp;&emsp; 类似于AbstractEventLoop.create_server(),但是具体的socket family是AF_UNIX。 &emsp;&emsp; 这个是一个协程方法。 &emsp;&emsp; 仅适用于:UNIX。 ### **1.8. 监视文件描述符(file descriptor)** 对于Windows上的[SelectorEventLoop](),只有socket处理被支持(例如:管道文件描述符不被支持)。 对于Windows上的[ProactorEventLoop](),这些方法都不被支持。 #### AbstractEventLoop.**add_reader**(fd, callback, \*args) &emsp;&emsp; 开始监视可读的文件描述符fd,然后会传递给定的args来调用callback。 &emsp;&emsp; (可以使用`functools.partial()`来给callback传递参数) #### AbstractEventLoop.**remove_reader**(fd) &emsp;&emsp; 停止监视这个可读的文件描述符。 #### AbstractEventLoop.**add_writer**(fd, callback, \*args) &emsp;&emsp; 开始监视可写的文件描述符fd,然后会传递给定的args来调用callback。 &emsp;&emsp; (可以使用`functools.partial()`来给callback传递参数) #### AbstractEventLoop.**remove_writer**(fd) &emsp;&emsp; 停止监视这个可写的文件描述符。 最下面有这些方法的实例。 ### **1.9. 低级socket操作** #### *coroutine* AbstractEventLoop.**sock_recv**(sock, nbytes) &emsp;&emsp; 从socket获取数据。仿照的socket.socket.recv()这个阻塞方法。 &emsp;&emsp; 返回值是bytes(字节)对象,代表接受的数据。一次接受的字节数的最大值由nbytes参数指定。 &emsp;&emsp; [SelectorEventLoop]()事件循环调用这个方法的时候,sock参数必须是非阻塞的socket。 &emsp;&emsp; 这个是一个协程方法。 #### *coroutine* AbstractEventLoop.**sock_sendall**(sock, data) &emsp;&emsp; 给socket发送数据。仿照的socket.socket.sendall()这个阻塞方法。 &emsp;&emsp; 这个socket必须连接一个远程的socket。这个方法持续的发送来自于data的数据,直到全部发送完毕或者有错误产生。成功时返回None。错误时会引发一个异常,并且此时没有办法确定多少数据(如果有的话)被成功地由该连接的接收端处理。 &emsp;&emsp; [SelectorEventLoop]()事件循环调用这个方法的时候,sock参数必须是非阻塞的socket。 &emsp;&emsp; 这个是一个协程方法。 #### *coroutine* AbstractEventLoop.**sock_connect**(sock, address) &emsp;&emsp; 连接到地址为address的远程socket上。仿照的socket.socket.connect()这个阻塞方法。 &emsp;&emsp; [SelectorEventLoop]()事件循环调用这个方法的时候,sock参数必须是非阻塞的socket。 &emsp;&emsp; 这个是一个协程方法。 &emsp;&emsp; *改动于3.5.2版本*:address参数不一定需要被解析。`socket_connect`会通过调用socket.inet_pton()来检查address是否被解析了。如果没有,AbstractEventLoop.getaddrinfo()将被用于解析这个address。 #### *coroutine* AbstractEventLoop.**sock_accept**(sock) &emsp;&emsp; 接受一个连接。仿照的socket.socket.accept()这个阻塞方法。 &emsp;&emsp; sock这个参数必须是已绑定到一个地址并且监听连接的socket。返回值是(conn, address)对,conn是一个新socket对象用于在连接上发送和接受数据,address是绑定到socket上的连接的另一端的地址。 &emsp;&emsp; sock参数必须是非阻塞的socket。 &emsp;&emsp; 这个是一个协程方法。 ### **1.10. 解析主机名** #### *coroutine* AbstractEventLoop.**getaddrinfo**(host, port, \*, family=0, type=0, proto=0, flags=0) &emsp;&emsp; 这是一个协程方法,类似于socket.getaddrinfo(),但是不阻塞。 #### *coroutine* AbstractEventLoop.**getnameinfo**(sockaddr, flags=0) &emsp;&emsp; 这是一个协程方法,类似于socket.getnameinfo(),但是不阻塞。 ### **1.11. 管道连接** Windows上的SelectorEventLoop不支持这些方法。Windows上的ProactorEventLoop支持这些方法。 #### *coroutine* AbstractEventLoop.**connect_read_pipe**(protocol_factory, pipe) &emsp;&emsp; 在事件循环里注册 读管道。 &emsp;&emsp; protocol_factory参数应该是带有[Protocol]()接口的实例对象。pipe参数应该是一个类似文件(file-like)的对象。返回(transport, protocol)对,transport支持[ReadTransport]()的接口。 &emsp;&emsp; [SelectorEventLoop]()事件循环调用这个方法的时候,pipe必须是非阻塞模式。 &emsp;&emsp; 这个是一个协程方法。 #### *coroutine* AbstractEventLoop.**connect_write_pipe**(protocol_factory, pipe) &emsp;&emsp; 在事件循环里注册 写管道。 &emsp;&emsp; protocol_factory参数应该是带有BaseProtocol接口的实例对象。pipe参数应该是一个类似文件(file-like)的对象。返回(transport, protocol)对,transport支持[WriteTransport]()的接口。 &emsp;&emsp; [SelectorEventLoop]()事件循环调用这个方法的时候,pipe必须是非阻塞模式。 &emsp;&emsp; 这个是一个协程方法。 > 可以参考[AbstractEventLoop.subprocess_exec()]()和[AbstractEventLoop.subprocess_shell()]()方法。 ### **1.12. UNIX信号** 仅适用于:UNIX。 #### AbstractEventLoop.**add_signal_handler**(signum, callback, \*args) &emsp;&emsp; 为信号添加一个处理程序。 &emsp;&emsp; 如果信号的数值是无效的或者获取不了,会引发ValueError异常。如果设置处理程序时出现问题,会引发RuntimeError异常。 &emsp;&emsp; (可以使用`functools.partial()`来给callback传递参数) #### AbstractEventLoop.**remove_signal_handler**(sig) &emsp;&emsp; 为信号移除处理程序。 &emsp;&emsp; 如果信号处理程序被移除,返回True,否则返回False。 > 可以参考官方文档的signal模块。 ### **1.13. 运行器(Executor)** 在[Executor]()(线程池或进程池)里调用函数。默认情况下,事件循环使用着一个线程池运行器([ThreadPoolExecutor]())。 #### *coroutine* AbstractEventLoop.**run_in_executor**(executor, func, \*args) &emsp;&emsp; 安排func在指定的运行器(executor)里调用。 &emsp;&emsp; executor参数应该是一个[Executor]()实例。如果executor参数为None,就使用默认运行器。 &emsp;&emsp; (可以使用`functools.partial()`来给func传递参数) &emsp;&emsp; 这个是一个协程方法。 #### AbstractEventLoop.**set_default_executor**(executor) &emsp;&emsp; 为run_in_executor()函数设置默认运行器。 ### **1.14. 错误处理API** 让你定制事件循环里的异常处理。 #### AbstractEventLoop.**set_exception_handler**(handler) &emsp;&emsp; 把handler设置为新的事件循环的异常处理程序。 &emsp;&emsp; 如果handler为None,就会设置为默认的异常处理程序。 &emsp;&emsp; 如果handler是*可被调用*(callable)对象,他应该匹配(loop, context)作为参数,其中loop是一个活跃的事件循环,context是一个dict对象(具体参考下面的call_exception_handler()方法里的context)。 #### AbstractEventLoop.**get_exception_handler**() &emsp;&emsp; 返回异常处理程序,如果使用的是默认的那个就会返回None。 &emsp;&emsp; *新增于3.5.2版本。* #### AbstractEventLoop.**default_exception_handler**(context) &emsp;&emsp; 默认的异常处理程序。 &emsp;&emsp; 这个函数在异常发生且没有设置异常处理程序时被调用;也能被自己定制的异常处理程序调用,如果你想推迟默认行为的话。 &emsp;&emsp; context参数和下面的call_exception_handler()里的context意义一样。 #### AbstractEventLoop.**call_exception_handler**(context) &emsp;&emsp; 调用当前事件循环的异常处理程序。 &emsp;&emsp; context是一个包含以下关键字作为key的dict(一些新的key可能会在后面介绍): + 'message': 错误消息; + 'exception'(可选): 异常对象; + 'future'(可选): asyncio.Future实例; + 'handle'(可选): asyncio.Handle实例; + 'protocol'(可选): Protocol(协议)实例; + 'transport'(可选): Transport(传输)实例; + 'socket'(可选): socket.socket实例。 > 注意:这个方法不应该被任何事件循环的子类重载。想使用自己定制的异常处理程序,请使用`set_exception_handler()`方法。 ### **1.15. 调试模式** #### AbstractEventLoop.**get_debug**() &emsp;&emsp; 得到事件循环是否调试模式(布尔值)。 &emsp;&emsp; 如果环境变量PYTHONASYNCIODEBUG被设置为一个非空的字符串,默认值是True;否则是False。 &emsp;&emsp; *新增于3.4.2版本。* #### AbstractEventLoop.**set_debug**(enabled: bool) &emsp;&emsp; 为事件循环设置调试模式。 &emsp;&emsp; *新增于3.4.2版本。* > 可以参考最后一章 使用asyncio开发 的 asyncio的调试模式 章节。 ### **1.16. 服务端(Server)** #### *class* asyncio.**Server** &emsp;&emsp; 监听socket的服务端。 &emsp;&emsp; 通过AbstractEventLoop.create_server()方法和start_server()方法来创建对象;不要直接实例化这个类。 &emsp;&emsp; **close**() &emsp;&emsp; &emsp;&emsp; 关闭服务:关闭监听的socket并且设置这些socket的属性为None。 &emsp;&emsp; &emsp;&emsp; 那些代表现有的客户端连接的socket就悬空了。 &emsp;&emsp; &emsp;&emsp; 服务端以同步方式关闭;使用wait_close()协程方法会等着服务端被关闭。 &emsp;&emsp; *coroutine* **wait_close**() &emsp;&emsp; &emsp;&emsp; 等待close()方法完成。 &emsp;&emsp; &emsp;&emsp; 这是一个协程方法。 &emsp;&emsp; **sockets** &emsp;&emsp; &emsp;&emsp; 服务端正在监听的socket.socket对象的list;如果已经关闭了,会得到None。 ### **1.17. Handle** #### *class* asyncio.**Handle** &emsp;&emsp; AbstractEventLoop.call_soon()、AbstractEventLoop.call_soon_threadsafe()、AbstractEventLoop.call_later()和AbstractEventLoop.call_at()的返回值,一个回调包装对象。 &emsp;&emsp; **cancel**() &emsp;&emsp; &emsp;&emsp; 关闭这个回调。如果这个回调已经关闭了或者已经运行了,这个方法无效。 ### **1.18. 事件循环实例** #### **1.18.1. 使用call_soon()的Hello World** 使用call_soon()方法来安排一个回调的实例。这个回调函数显示“Hello World”然后停止事件循环。 ```python import asyncio def hello_world(loop): print('Hello World') loop.stop() loop = asyncio.get_event_loop() # 安排调用hello_world() loop.call_soon(hello_world, loop) # 阻塞调用由loop.stop()中断 loop.run_forever() loop.close() ``` > 也可以查看使用协程的[Hello World协程实例]()。 #### **1.18.2. 使用call_later()显示当前时间** 通过回调方式每秒钟显示当前时间的实例。在五秒钟之内,回调使用call_later()方法来安排它自己然后停止事件循环。 ```python import asyncio import datetime def display_date(end_time, loop): print(datetime.datetime.now()) if (loop.time() + 1.0) < end_time: loop.call_later(1, display_date, end_time, loop) else: loop.stop() loop = asyncio.get_event_loop() # 安排第一次调用display_date() end_time = loop.time() + 5.0 loop.call_soon(display_date, end_time, loop) # 阻塞调用由loop.stop()中断 loop.run_forever() loop.close() ``` > 也可以查看使用协程的[显示当前时间协程实例]()。 #### **1.18.3. 监视文件描述符的读取事件** 使用add_reader()方法等待直到文件描述符获取了一些数据,然后停止事件循环。 ```python import asyncio try: from socket import socketpair except ImportError: from asyncio.windows_utils import socketpair # 创建一对连接的文件描述符 rsock, wsock = socketpair() loop = asyncio.get_event_loop() def reader(): data = rsock.recv(100) print("Received:", data.decode()) # 我们完成了:注销文件描述符 loop.remove_reader(rsock) # 停止事件循环 loop.stop() # 注册文件描述符的读事件 loop.add_reader(rsock, reader) # 模拟来自网络的数据接收 loop.call_soon(wsock.send, 'abc'.encode()) # 运行事件循环 loop.run_forever() # 我们完成了:关闭socket和事件循环 rsock.close() wsock.close() loop.close() ``` #### **1.18.4. 为SIGINT和SIGTERM设置信号处理程序** 使用add_signal_handler()方法为SIGINT和SIGTERM信号量注册处理程序。 ```python import asyncio import functools import os import signal def ask_exit(signame): print("got signal %s: exit" % signame) loop.stop() loop = asyncio.get_event_loop() for signame in ('SIGINT', 'SIGTERM'): loop.add_signal_handler(getattr(signal, signame), functools.partial(ask_exit, signame)) print("Event loop running forever, press Ctrl+C to interrupt.") print("pid %s: send SIGINT or SIGTERM to exit." % os.getpid()) try: loop.run_forever() finally: loop.close() ``` 这个例子仅仅工作在UNIX上。