在这一章中,将使用asyncio写一个TCP服务器。这个服务器的作用是通过规范名称查找Unicode字符,来看下代码:
import asyncio
from charfinder import UnicodeNameIndex
CRLF=b' '
PROMPT=b'?>'
index=UnicodeNameIndex()
@asyncio.coroutine
def handle_queries(reader,writer):
while True:
writer.write(PROMPT)
yield from writer.drain()
data=yield from reader.readline()
try:
query=data.decode().strip()
except UnicodeDecodeError:
query='x00'
client=writer.get_extra_info('peername')
print('Received from {}:{!r}'.format(client,query))
if query:
if ord(query[:1])<32:
break
lines=list(index.find_description_strs(query))
if lines:
writer.writelines(line.encode()+CRLF for line in lines)
writer.write(index.status(query,len(lines)).encode()+CRLF)
yield from writer.drain()
print('Sent {} results'.format(len(lines)))
print('Close the client socket')
writer.close()
def main(address='127.0.0.1',port=2323):
port=int(port)
loop=asyncio.get_event_loop()
server_coro=asyncio.start_server(handle_queries,address,port,loop=loop)
server=loop.run_until_complete(server_coro)
hosts=server.sockets[0].getsockname()
print('Serving on {}. Hit CTRL_C to stop'.format(hosts))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
print('Server shutting down')
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
if __name__=="__main__":
main()
main函数的运行过程如下:
(1)在main中默认两个参数:address和port。
(2)asyncio.start_server的协程运行完后,返回的协程对象返回一个asyncio.Server实例,这个实例是一个TCP套接字服务器,封装了诸如socket函数,并且会给handle_queries传递StreamWriter.write和StreamReader.readline进行读写操作。
(3)server=loop.run_until_complete(server_coro)驱动协程,启动服务器
(4)获取这个服务器的第一个套接字的地址和端口
(5)loop.run_forever() 运行事件循环,main在这里被阻塞,直到服务器的控制台中按下CTRL-C键才会关闭
(6)最后关闭服务器,终止事件循环
来看下handle_queries的操作过程:
(1)函数接收2个参数,reader和writer,在asyncio.start_server中传递进来
(2)writer.write(PROMPT),这个其实是StreamWriter.write, 这个方法不是协程因此不能采用yield from。这个是给终端界面输出?>
(3) writer.drain刷新writer缓冲,因为它是协程,因此采用yield from
(4)yield from reader.readline()是从缓冲区读取字符,这个方法是一个协程,返回一个bytes对象
(5)writer_get_extra_info返回的是与套接字连接的远程地址
(6) 下面是输出查询到的字符到终端并在每行末尾添加了回车符和换行符
writer.writelines(line.encode()+CRLF for line in lines)
writer.write(index.status(query,len(lines)).encode()+CRLF)
(7)关闭StreamWriter流
代码运行效果如下,当输入chess black的时候会返回查询到的结果
从服务器代码中可以看到打印的如下信息
/usr/bin/python3.6 /home/zhf/py_prj/function_test/asy_server_try.py
Serving on ('127.0.0.1', 2323). Hit CTRL_C to stop
Received from ('127.0.0.1', 46670):'chess black'
Sent 6 results