• Python之数据流(stream)


      本文参考Python官方文档:https://docs.python.org/zh-cn/3.8/library/asyncio-stream.html

      本文参考Python官方文档针对官方文档示例进行解析,解析不完整只为了便于理解

      流

      流是用于处理网络连接的高级async/await-ready原语。流允许发送和接收数据,而不需要使用回调或低级协议和传输。

      Stream函数

      下面的高级 asyncio 函数可以用来创建和处理流:coroutine asyncio.open_connection(host=Noneport=None*loop=Nonelimit=Nonessl=Nonefamily=0proto=0flags=0sock=Nonelocal_addr=Noneserver_hostname=Nonessl_handshake_timeout=None)

    建立网络连接并返回一对 (reader, writer) 对象。

    返回的 reader 和 writer 对象是 StreamReader 和 StreamWriter 类的实例。

    注意:使用ayncio.open_connection()方法创建和处理流时只有在await时才返回reader和writer对象

    为了方便测试我们在本地搭建一个nginx服务器,首页index.html内容为“Hello World”

     示例:

    import asyncio
    async def wget(host):
        connect = asyncio.open_connection(host,80)
        print(type(connect))
        reader,writer = await connect
        print(type(reader),type(writer))
           
    async def main():
        # 获取表头主机列表
        hosts = ['192.168.1.100']
       # 根据主机列表获取一个tasks列表
        tasks = [asyncio.create_task(wget(host)) for host in hosts]
        # 等待任务列表执行结果
        await asyncio.gather(*tasks)
    
    # 运行
    asyncio.run(main() 
    

      

    运行输出如下

    <class 'coroutine'>
    <class 'asyncio.streams.StreamReader'> <class 'asyncio.streams.StreamWriter'>
    Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x00000236AA956F70>
    Traceback (most recent call last):
      File "C:ProgramDataAnaconda3libasyncioproactor_events.py", line 116, in __del__
        self.close()
      File "C:ProgramDataAnaconda3libasyncioproactor_events.py", line 108, in close
        self._loop.call_soon(self._call_connection_lost, None)
      File "C:ProgramDataAnaconda3libasyncioase_events.py", line 719, in call_soon
        self._check_closed()
      File "C:ProgramDataAnaconda3libasyncioase_events.py", line 508, in _check_closed
        raise RuntimeError('Event loop is closed')
    RuntimeError: Event loop is closed
    

      

    解析:运行报错是因为获取了writer对象但是并没有写数据

    打印类型可以看到

     完善代码使用writer对象发送请求至服务器,然后reader对象就可以收取服务器发送过来的数据

    # asyncio.open_connection创建数据流 start
    import asyncio
    async def wget(host):
        connect = asyncio.open_connection(host,80)
        print(type(connect))
        reader,writer = await connect
        print(type(reader),type(writer))
        # 定义请求头部,格式是固定格式
        header = 'GET / HTTP/1.0
     Host:{0}
    
    '.format(host)
        # 通过writer对象往http服务器发送请求,请求是二进制格式的需要使用encode()方法编码
        writer.write(header.encode('utf-8'))
        # writer.write方法需要与drain()方法一起使用
        await writer.drain()
        # 阻塞获取服务器发送过来的所有数据,read()方法一次性获取所有数据,数据多可以使用readline()方法一行行获取
        data = await reader.read()
        # 打印获取的数据,获取数据为二进制格式不加decode()解码则打印原始数据
        print(data.decode())
        # 关闭writer需要和writer.wait_closed()一起使用,这里可以省略
        writer.close()
        await writer.wait_closed()
           
    async def main():
        hosts = ['192.168.1.100']
        tasks = [asyncio.create_task(wget(host)) for host in hosts]
        await asyncio.gather(*tasks)
    
    asyncio.run(main())
    # asyncio.open_connection创建数据流 end
    

      输出如下

    <class 'coroutine'>
    <class 'asyncio.streams.StreamReader'> <class 'asyncio.streams.StreamWriter'>
    HTTP/1.1 200 OK
    Server: nginx/1.14.0
    Date: Sat, 30 Oct 2021 09:37:17 GMT
    Content-Type: text/html
    Content-Length: 12
    Last-Modified: Fri, 29 Oct 2021 07:41:00 GMT
    Connection: close
    ETag: "617ba58c-c"
    Accept-Ranges: bytes
    
    Hello World
    

      本次代码演示了连接http服务器并且向服务器发送一个GET请求,服务器收到GET请求以后把数据返回给客户,然后通过reader对象获取到服务器发送过来的数据。

      注意:本次发送的是一个GET请求,格式是固定的

     本次发送的完整数据为

    'GET / HTTP/1.0
     Host:192.168.1.100
    
    '
    

      对应关系如下图

     拆分解析如下

    GET / HTTP/1.0
     Host:192.168.1.100
    
    
    GET #请求方法为GET 
     # 空格
    / # 请求URL为/即根目录
    HTTP/1.0 # 协议版本
    
     # 回车符和换行符
    Host # 头部字段为Host
    : # 固定格式的符号:
    192.168.1.100 # Host的值,即本次请求的主机值
    
     # 请求头部的回车符和换行符
    
     # 最后的回车符和换行符
    

      使用流的TCP回显客户端和服务器

    使用流的TCP回显客户端

    tcp_stream_client.py

    import asyncio
    
    # 回显客户端协程函数,传递参数message发送给服务器端,服务器端接收信息原样返回
    async def tcp_echo_client(message):
        # 创建reader,writer对象分别用于接收和发送信息
        reader,writer = await asyncio.open_connection('127.0.0.1',8888)
        print(f'Send:{message!r}')
        # 往服务器端写信息,需要编码后发送
        writer.write(message.encode())
        # await writer.drain()
        # 从服务器端读取信息读取100个字节
        data = await reader.read(100)
        # 打印解码后的信息
        print(f'Received:{data.decode()!r}')
        print('Close the connection')
        # 关闭
        writer.close()
        # await writer.wait_closed()
    
    asyncio.run(tcp_echo_client('Hello World!'))
    

      注意:这里没有使用writer.drain()和writer.wait_closed()也可以

      

    使用流的TCP回显服务器端

      tcp_stream_server.py

    import asyncio
    
    # 启动服务后当客户端建立新连接时调用该函数
    # 接受参数为reader,writer
    # reader是类StreamReader的实例,而writer是类StreamWriter的实例
    # 即客户端和服务器端的reader和writer是一一对应的,分别用于接收对方数据流和往对方发送数据流
    async def handle_echo(reader, writer):
        # 服务器从客户端读取信息
        # 即客户端通过writer往服务器写的信息
        data = await reader.read(100)
        # 信息解码
        message = data.decode()
        # 该方法获取客户端的ip地址信息
        addr = writer.get_extra_info('peername')
    
        print(f"Received {message!r} from {addr!r}")
    
        print(f"Send: {message!r}")
        # 服务器端把从客户端读取的信息又发送给客户端
        writer.write(data)
        await writer.drain()
        # 关闭连接
        print("Close the connection")
        writer.close()
    
    async def main():
        # start_server()方法启动套接字服务,返回一个server对象
        # 当一个新的客户端连接被建立时,回调函数会被调用。该函数会接收到一对参数(reader,writer)
        # reader是类StreamReader的实例,而writer是类StreamWriter的实例
        # client_connected_cb 即可以是普通的可调用对象也可以是一个 协程函数; 如果它是一个协程函数,它将自动作为 Task 被调度。
        server = await asyncio.start_server(handle_echo, '127.0.0.1', 8888)
        # 以下方法可以获取启动的ip地址和端口信息返回一个元组其实即使start_server方法传递的ip和端口信息('127.0.0.1',8888)
        addr = server.sockets[0].getsockname()
        print(f'Serving on {addr}')
        # 启动服务端
        # Server对象是异步上下文管理器。当用于async with语句时,异步上下文管理器可以确保Server对象被关闭
        # 并且在async with完成后不接受新的连接。
        async with server:
            # server_forver()方法
            # 开始接受连接,直到协程被取消。server_forever任务的取消将导致服务器被关闭
            await server.serve_forever()
    
    asyncio.run(main())
    

      

      打开两个窗口,先启动服务器端

     

     服务器端开启了8888端口等待客户端连接

    运行客户端

    运行客户端的时候客户端和服务器端建立了连接才启动协程函数handle_echo

     

     服务器端接收的信息为

  • 相关阅读:
    python学习之__doc__,__module__,__class__,__del__,__call__,__iter__,__next__
    20180910
    20100911
    20180912
    服装分销
    道讯商品条码管理
    [新版新概念英语14册全部视频和课本]
    2012/06/18
    《鬼谷子本经阴符七术》
    归来
  • 原文地址:https://www.cnblogs.com/minseo/p/15485571.html
Copyright © 2020-2023  润新知