• 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()
    
  • 相关阅读:
    Netty回调与Channel执行流程分析
    Netty执行流程分析与重要组件介绍
    HBase 介绍
    Java文件上传下载原理
    ngxtop安装和使用
    开启Nginx监控 with-http_stub_status_module
    Spring 事务模板方法设计模式
    Spring 事务管理
    JdkDynamicAopProxy 拦截器链的获得与递归执行
    CgLib实现AOP
  • 原文地址:https://www.cnblogs.com/c-x-a/p/10697256.html
Copyright © 2020-2023  润新知