asyncio协议

服务端

import asyncio
import logging
import sys
from typing import Optional

SERVER_ADDRESS = ('localhost', 10000)

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


class EchoServer(asyncio.Protocol):
    """
    每个新客户端连接都会触发对connection_made()的调用。
    transport参数是asyncio.Transport的一个实例,它提供了使用套接字进行异步I/O的抽象。
    不同类型的通信提供不同的传输实现,所有这些都具有相同的API。
    例如,有单独的传输类用于处理套接字和处理管道到子进程。
    传入客户端的地址可通过get_extra_info()传输获得,
    get_extra_info()是一种特定于实现的方法。

    启动顺序
    State machine of calls:

     start -> CM [-> DR*] [-> ER?] -> CL -> end

    * CM: connection_made()
    * DR: data_received()
    * ER: eof_received()
    * CL: connection_lost()
    """

    def connection_made(self, transport):
        self.transport = transport
        self.address = transport.get_extra_info('peername')
        self.log = logging.getLogger('EchoServer_{}_{}'.format(*self.address))
        self.log.debug('connection accepted')

    def data_received(self, data: bytes) -> None:
        """
        类似于一个循环,循环调用这个方法读取内容。
        """
        self.log.debug('received{!r}'.format(data))
        self.transport.write(data)
        self.log.debug('sent {!r}'.format(data))

    def eof_received(self) -> Optional[bool]:
        """
        有些传输支持特殊的文件结束指示符(“EOF”)。
        遇到EOF时,调用eof_received()方法。在这个实现中,
        EOF被发送回客户端以指示它被接收到。
        因为不是所有的传输都支持显式的EOF,
        所以该协议首先询问传输发送EOF是否安全。
        :return:
        """
        self.log.debug('received EOF')
        if self.transport.can_write_eof():
            self.transport.write_eof()

    def connection_lost(self, exc: Optional[Exception]) -> None:
        """
        当连接关闭时,无论是正常情况还是由于错误,
        都会调用协议的connection_lost()方法。
        如果有错误,参数包含适当的异常对象。否则就没有了。
        :param exc: 
        :return:
        """
        if exc:
            self.log.error('ERROR:{}'.format(exc))
        else:
            self.log.debug('closing')
        super().connection_lost(exc)


if __name__ == '__main__':
    """
    然后需要运行事件循环以处理事件和处理客户端请求。 
    对于长时间运行的服务,run_forever()方法是执行此操作的最简单方法。 
    当事件循环被应用程序代码或通过发送进程信号停止时,
    可以关闭服务器以正确清理套接字,
    然后可以关闭事件循环以在程序退出之前完成处理任何其他协同程序。
    """
    loop = asyncio.get_event_loop()
    factory = loop.create_server(EchoServer, *SERVER_ADDRESS)
    server = loop.run_until_complete(factory)
    log.debug('starting up on {} port {}'.format(*SERVER_ADDRESS))
    try:
        loop.run_forever()
    finally:
        log.debug('closing server')
        server.close()
        loop.run_until_complete(server.wait_closed())
        log.debug('closing event loop')
        loop.close()

客户端

import asyncio
import functools
import logging
import sys
from asyncio import transports
from typing import Optional

MESSAGES = [
    b'This is the message. ',
    b'It will be sent ',
    b'in parts.',
]
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()


class EchoClient(asyncio.Protocol):
    """
    客户机协议类定义了与服务器相同的方法,但实现不同。
    类构造函数接受两个参数,一个是要发送的消息列表,
    另一个是将来要使用的实例,通过接收来自服务器
    的响应来表示客户机已经完成了一个工作周期。
    """

    def __init__(self, messages, future):
        super().__init__()
        self.messages = messages
        self.log = logging.getLogger('EchoClient')
        self.f = future

    def connection_made(self, transport: transports.BaseTransport) -> None:
        self.transport = transport
        self.address = transport.get_extra_info('peername')
        self.log.debug('connecting to {} port {}'.format(*self.address))
        for msg in self.messages:
            transport.write(msg)
            self.log.debug('sending {!r}'.format(msg))
        if transport.can_write_eof():
            transport.write_eof()
    def data_received(self, data: bytes) -> None:
        self.log.debug("received {!r}".format(data))

    def eof_received(self) -> Optional[bool]:
        self.log.debug('received EOF')
        self.transport.close()
        if not self.f.done():
            self.f.set_result(True)

    def connection_lost(self, exc: Optional[Exception]) -> None:
       self.log.debug('server closed connection')
       self.transport.close()
       if not self.f.done():
           self.f.set_result()
       super().connection_lost(exc)

client_completed = asyncio.Future()

client_factory = functools.partial(
    EchoClient,
    messages=MESSAGES,
    future=client_completed,
)
factory_coroutine = event_loop.create_connection(
    client_factory,
    *SERVER_ADDRESS,
)
log.debug('waiting for client to complete')
try:
    event_loop.run_until_complete(factory_coroutine)
    event_loop.run_until_complete(client_completed)
finally:
    log.debug('closing event loop')
    event_loop.close()
posted @ 2019-04-12 17:14  公众号python学习开发  阅读(517)  评论(0编辑  收藏  举报