asyncio延迟了StreamReader.read

时间:2015-11-02 17:44:06

标签: pygame python-asyncio python-3.5

我对asyncio和async def / await语法都不熟悉,所以我想问一下我该怎样做这样的事情:

import asyncio
import pygame
import logging
from pygame import *
log = logging.getLogger('')


class Client:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.loop = asyncio.get_event_loop()
        self.loop.run_until_complete(self.create_client())

    async def create_client(self):
        self.reader, self.writer = await asyncio.open_connection(self.host, 
                                                                 self.port, 
                                                                 loop=self.loop)
        asyncio.ensure_future(self._handle_packets(), loop=self.loop)

    async def _handle_packets(self):
        while True:
            data = await self.reader.read(4096)
            if not data:
                continue
            message = data.decode()
            log.debug("(NET) Received "+message)

    def send(self, data):
        self.loop.run_until_complete(asyncio.ensure_future(self._send(data),
                                                           loop=self.loop))

    async def _send(self, data):
        self.writer.write(data)
        await self.writer.drain()
        print("_send done")

    def disconnect(self):
        print("DC")
        self.loop.close()


def main():
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    pygame.display.set_caption("Pyond client")
    bg = Surface((640, 480))
    bg.fill(Color("#004400"))
    client = Client('127.0.0.1', 2508)
    while True:
        pygame.event.pump()
        for e in pygame.event.get():
            if e.type == QUIT:
                raise SystemExit
            elif e.type == KEYUP:
                if e.key == K_UP:
                    client.send(b"{'Hello':'World'}")
        screen.blit(bg, (0, 0))
        pygame.display.update()
    client.disconnect()


if __name__ == "__main__":
    main()

此代码使用pygame创建640x480窗口,然后读取传入的K_UP(向上箭头)键。按下后,将类似json的字符串发送到服务器。 _handle_packets应该从服务器读取任何传入的数据并打印它。 我正在测试这段代码并且发送工作还可以,但接收时间已经很晚了。我确定我需要将处理程序放在其他地方,所以究竟在哪里? 顺便说一句,发送只工作一次。也需要帮助。

1 个答案:

答案 0 :(得分:1)

这里有几个问题。

第一个非常基础asycnio事件循环在create_client()完成后停止运行,并且仅在 send()数据时再次运行。因此,只有_handle_packets才能运行send()。理想情况下,您应该在更高的范围内启动一次事件循环,并在完成所有操作后关闭它。

第二个问题是,无论何时client.send(b"{'Hello':'World'}"),您都会阻止外部pygame while True循环,从而阻止处理任何其他事件,直到发送上一个事件为止。您应该使用asyncio.Queue对事件进行排队&从Client班级发送。

以下是我要做的一些更改(抱歉,未经测试;我没有pygame已安装ATM):

# vim: tabstop=4 expandtab

import asyncio
import pygame
import logging
from pygame import *
log = logging.getLogger('')


class Client:
    def __init__(self, host, port, loop):
        self.host = host
        self.port = port
        self.loop = loop
        self.send_q = asyncio.Queue()

    async def connect(self):
        self.reader, self.writer = await asyncio.open_connection(self.host, 
                                                                 self.port, 
                                                                 loop=self.loop)
        self.loop.create_task(self._handle_packets())
        self.loop.create_task(self._send())

    async def _handle_packets(self):
        while True:
            data = await self.reader.read(4096)
            if not data:
                continue
            message = data.decode()
            log.debug("(NET) Received "+message)

    def send(self, data):
        self.send_q.put_nowait(data)

    async def _send(self):
        while True:
            data = await self.send_q.get()
            self.writer.write(data)
            await self.writer.drain()

    def disconnect(self):
        print("DC")
        self.writer.close()


async def main(loop):
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    pygame.display.set_caption("Pyond client")
    bg = Surface((640, 480))
    bg.fill(Color("#004400"))
    client = Client('127.0.0.1', 2508, loop)
    await client.connect()
    while True:
        pygame.event.pump()
        for e in pygame.event.get():
            if e.type == QUIT:
                raise SystemExit
            elif e.type == KEYUP:
                if e.key == K_UP:
                    client.send(b"{'Hello':'World'}")
        screen.blit(bg, (0, 0))
        pygame.display.update()
    client.disconnect()


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main(loop))
    loop.close()

要记住的另一个重要事项是,您永远不应该使用asyncio阻止pygame事件循环,否则后台中的Client网络处理会失速。我从未使用过pygame,因此我不熟悉哪个pygame功能可能会阻塞"但是应该使用{{1}来调用它们}。这将在另一个线程中调用阻塞函数。