2015-11-02 55 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應該讀取來自服務器的任何傳入數據,並將其打印出來。 我正在測試此代碼併發送工作正常,但接收延遲很大。我確定我需要把處理程序放在別的地方,所以到底在哪裏? 而順便說一句,只發送作品一次。這個也需要幫助。

回答

1

這裏有幾個問題。

第一個很漂亮基本。在create_client()完成後,asycnio事件循環停止運行,並且只能再次運行,而您的send()數據。所以,唯一能夠運行_handle_packets的時間是當你是send() ing。理想情況下,您應該在更高的範圍內啓動事件循環,並在完成所有事情後關閉它。

第二個問題是,只要您client.send(b"{'Hello':'World'}"),您將阻止外部pygamewhile 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,否則在後臺的網絡處理將會失速。我從來沒有使用pygame,所以我不熟悉哪個pygame函數可能是「阻塞」的,但應該使用result = await loop.run_in_executor(None, blocking_func, *func_args)來調用它們。這將在另一個線程中調用阻塞函數。