第十章:使用进程、线程和协程提供并发性-asyncio:异步I/O、事件循环和并发工具-使用协程和流的异步I/O-回送服务器

10.5.9 使用协程和流的异步I/O
这一节以上两个示例程序的另一个版本(分别实现一个简单的回送服务器和客户端),这里会使用协程和asyncio流API,而不是使用协议和传输类抽象。与protocol API相比,这些例子在更低的抽象层上操作,不过事件的处理是类似的。

10.5.9.1 回送服务器
服务器首先导入建立asyncio和logging所需的模块,然后创建一个事件循环对象。

import asyncio
import logging
import sys

SERVER_ADDRESS = ('localhost',10000)
logging.basicConfig(
    level=logging.DEBUG,
    format='%(name)s:%(message)s',
    stream=sys.stderr,
    )
log = logging.getLogger('main')

event_loop = asyncio.get_event_loop()

# 然后服务器定义一个协程来处理通信。每次一个客户端连接时,都会调用这个协程的一个
# 新实例;因此,在这个函数中,代码一次只与一个客户端通信。Python的语言运行时会
# 管理各个协程实例的状态,所以应用代码不需要管理任何额外的数据结构来跟踪各个
# 客户端。
# 协程的参数是与这个新连接关联的StreamReader和StreamWriter实例。与Transprot一个,
# 可以通过书写器的get_extra_info()方法访问客户端的地址。

async def echo(reader,writer):
    address = writer.get_extra_info('peername')
    log = logging.getLogger('echo_{}_{}'.format(*address))
    log.debug('connection accepted')

# 尽管建立连接时会调用洗车,但此时还没有任何要读取的数据。为了避免读取时阻塞,
# 协程对read()调用了await,从而允许事件循环继续处理其他任务,直到有可以读取的
# 数据。

    while True:
        data = await reader.read(128)

# 如果客户端发送了数据,则其会从await返回,并且通过把这个数据传递到书写器从而
# 发回给客户端。可以使用多个write()调用来缓冲发出的数据,然后使用drain()刷新
# 输出结果。由于刷新输出网络I/O可能阻塞,所以再次使用await恢复对事件循环的控制,
# 它会监视写套接字,并在可以发送更多数据时调用书写器。
    if data:
        log.debug('received {!r}'.format(data))
        writer.write(data)
        await writer.drain()
        log.debug('sent {!r}'.foramt(data))

# 如果客户端还没有发送任何数据,那么read()返回一个空字节串来指示连接关闭。服务
# 器需要关闭写客户端的套接字,然后协程返回,以指示它已经完成。
    else:
        log.debug('closing')
        writer.close()
        return

# 启动服务器有两个步骤。首先,应用告诉时间循环使用协程、主机名以及监听的套接字来
# 创建一个新的服务器对象。start_server()方法本社是一个协程,所以必须由事件循环
# 处理结果来具体启动服务器。完成协程时会生成一个绑定到事件循环的asyncio.Server
# 实例。
# Create the server and let the loop finish the coroutine before
# starting the real event loop.
factory = asyncio.start_server(echo,*SERVER_ADDRESS)
server = event_loop.run_until_complete(factory)
log.debug('starting up on {} prot {}'.format(*SERVER_ADDRESS))

# 接下来,需要允许事件循环来处理事件和客户请求。对于一个长时间运行的服务,
# run_forever()方法是完成这个工作最简单的方法。事件循环结束时(可能由应用代码结
# 束,也可能通过向进程发出信号来结束),可以关闭服务器以适当地清理套接字。然后
# 可以关闭事件循环,在程序退出前完成所有其他协程的处理。
# Enter the event loop permanently to handle all connections.
try:
    event_loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    log.debug('close server')
    server.close()
    event_loop.run_until_complete(server.wait_closed())
    log.debug('closing event loop')
    event_loop.close()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值