2015-11-02 2 views
1

Я своего рода новым для asyncio и асинхронной четкости/Await синтаксис, поэтому я хотел бы спросить, как именно я должен сделать что-то вроде этого:asyncio задерживается StreamReader.read

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() 

Этот код создает окно с разрешением 640x480 Pygame , затем считывает входящую клавишу K_UP (стрелка вверх). После нажатия посылает на сервер json-подобную строку. _handle_packets должен был читать любые входящие данные с сервера и просто печатать. Я тестировал этот код, и отправка работает нормально, но получение довольно отложено. Я уверен, что мне нужно положить обработчик где-то в другом месте, и где именно? И btw, отправлять работает только один раз. Нужна помощь и в этом.

ответ

1

Пара проблем здесь.

Первый довольно фундаментальный. Цикл событий asycnio прекращает работу после завершения create_client() и снова запускается только , а вы send() данных. Таким образом, единственный раз, когда он может работать _handle_packets - это когда вы находитесь send() ing. В идеале вы должны запустить цикл событий один раз в более высокой области и закрыть его, как только все закончится.

Вторая проблема заключается в том, что всякий раз, когда вы client.send(b"{'Hello':'World'}"), вы блокируете внешний цикл pygamewhile True, предотвращая обработку любых других событий до тех пор, пока не будет отправлен предыдущий. Вы должны использовать asyncio.Queue для очереди событий & отправьте их из класса Client.

Вот некоторые изменения, я хотел бы сделать (извините, непроверенный, я не pygame установлен банкомат):

# 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 может быть «блокирована», но их следует вызывать с помощью result = await loop.run_in_executor(None, blocking_func, *func_args). Это вызовет блокирующие функции в другом потоке.

Смежные вопросы