本文参考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”

Python之数据流(stream)_服务器

 

 示例:

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:\ProgramData\Anaconda3\lib\asyncio\proactor_events.py", line 116, in __del__
    self.close()
  File "C:\ProgramData\Anaconda3\lib\asyncio\proactor_events.py", line 108, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "C:\ProgramData\Anaconda3\lib\asyncio\base_events.py", line 719, in call_soon
    self._check_closed()
  File "C:\ProgramData\Anaconda3\lib\asyncio\base_events.py", line 508, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

  

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

打印类型可以看到

Python之数据流(stream)_get请求_02

 

 完善代码使用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\r\n Host:{0}\r\n\r\n'.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请求,格式是固定的

Python之数据流(stream)_html_03

 

 本次发送的完整数据为

'GET / HTTP/1.0\r\n Host:192.168.1.100\r\n\r\n'

  对应关系如下图

Python之数据流(stream)_服务器_04

 

 拆分解析如下

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