一、asyncio异步通信
学习博客/文档归类一下放在下面:
简单易懂的例子+各个函数详解:
Python 使用asyncio tcp
协程等概念解释+github实例:
python 异步socket编程
官方文档翻译:
asyncio异步IO——Streams详解
详细函数解释:
Python asyncio 异步编程(三)
二、解决asyncio.StreamReader读取长串数据
最近用asyncio做了异步socket tcp通信,在使用asyncio.StreamReader对象读取数据时出现了一点问题。
首先一些基本代码都是学习网上的,客户端代码:
reader, writer = await asyncio.open_connection(
'127.0.0.1', 8888)
但我发现如果服务端给客户端发的信息过长,
reader.read()
函数就不起作用,虽然官网对这个函数的解释是参数什么都不写就会读取所有的数据。网络查了一下也只发现一个解决办法(图中他其实遇到了跟我一样的问题也就是一调用
reader.read()
就会永久阻塞,并不会返回什么接收到的数据):
但是我一用yield就出错
后来想了想干脆用
at_eof()
这个函数吧,但怎么打印它都返回False也就缓冲区不为空。看官方文档对这个函数的解释:
如果缓冲区为空并且 feed_eof() 被调用,则返回 True 。
查了半天也没找到
feed_eof()
这个函数到底干什么用,但在
at_eof()
之前调用一下就解决一切问题了。
官网解释:
feed_eof()
Acknowledge the EOF.
具体代码:(但并没有解决根本原因,第一次对方发送消息可以正常接收,但之后发送消息就会报错
feed_data after feed_eof
的错)
async def recv_msg(self):
while True:
data = bytearray()
while True:
chunk = await self.reader.read(100)
if not chunk:
break
data += chunk
self.reader.feed_eof()
if self.reader.at_eof():
print(data) #这里就是一个完整的接收数据了
这么一看
if not chunk
好像也能直接判断缓冲区是不是空的。。。。并且发现
await self.reader.read(100)
好像不会因为对方没发送数据它就一直阻塞,而是会一直读取一直读取。那await这个功能意义何在?
feed_of()
这个函数的发现权当我减少运用了一次
await self.reader.read(100)
吧
上述问题的出现我归结于用了
feed_eof()
这个函数,我现在还不懂这个函数的作用。但不使用它之前我可以确定的是:
-
at_eof()
永远返回False -
await self.reader.read(100)
会在对方没有发消息的时候一直阻塞,而不是不断返回None或之类
所以之后我还是改用
self.reader.read(100)
这种低级的方法了,只能将参数改大,保证大于对方会发送的最长消息的字节数。如果以后需要传输文件的话就麻烦了需要再想想办法。但其实还有一个弱智办法是让对方先把文件大小传输过来,我再动态改变参数,应该是可行的。
上述方法又产生一个问题,如果参数设置过大,对方发送信息又过快,接收消息会产生TCP粘包的情况,再找一个解决方法就是使用
await self.reader.readuntil(seperator = b"\xFF\xFF")
,让发送方在每条发送数据包的后面都加上这个seperator,亲测有效解决问题。
三、一些想整理的
客户端判断是否还有在连接服务器,可以用
writer.is_closing()
,把writer设为类的属性,这样在不同的async函数里都可以获取,以控制reader函数,或者说整个的循环连接服务器的函数得以在连接断了之后,或是直接退出函数执行,或是开始不断连接服务器
writer函数肯定是放在一个循环中不断写入消息的,这个循环一定要写个
asyncio.sleep()
,以时不时的暂停一下,不然有可能会陷入死循环。
reader函数则不用
asyncio.sleep()
,它的
await self.reader.readuntil(...)
会在消息来的时候才执行,不然就停滞在这里并把执行权力让给其他协程。
客户端/服务器的例子,完整的代码