2015-03-16 18 views
13

Uczę się o bibliotece asyncio Pythona 3, a ja natknąłem się na mały problem. Próbuję dostosować przykład EchoServer z dokumentacji Pythona, aby monitować o dane wejściowe użytkownika, a nie tylko echo, co klient wysyła.Pytaj o dane wejściowe użytkownika za pomocą pythona instancja asyncio.create_server

Myślałem, że to będzie tak proste, jak tylko dodanie wywołania wejściowego(), ale oczywiście wejście() będzie blokować, dopóki nie pojawi się użytkownik, który powoduje problemy.

Idealnie chciałbym otrzymywać dane od klienta nawet wtedy, gdy serwer nie ma nic do "powiedzenia". Trochę jak klient czatu, gdzie każde połączenie rozmawia z serwerem. Chciałbym móc przełączać się pomiędzy poszczególnymi połączeniami i wysyłać dane wejściowe zgodnie z potrzebą ze standardowego wejścia. Prawie jak klient czatu P2P.

Rozważmy następujący zmodyfikowanego kodu EchoServer:

import asyncio 

class EchoServerClientProtocol(asyncio.Protocol): 
    def connection_made(self, transport): 
     peername = transport.get_extra_info('peername') 
     print('Connection from {}'.format(peername)) 
     self.transport = transport 

    def data_received(self, data): 
     message = data.decode() 
     print('Data received: {!r}'.format(message)) 

     reply = input() 
     print('Send: {!r}'.format(reply)) 
     self.transport.write(reply.encode()) 

     #print('Close the client socket') 
     #self.transport.close() 

loop = asyncio.get_event_loop() 
# Each client connection will create a new protocol instance 
coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888) 
server = loop.run_until_complete(coro) 

# Serve requests until CTRL+c is pressed 
print('Serving on {}'.format(server.sockets[0].getsockname())) 
try: 
    loop.run_forever() 
except KeyboardInterrupt: 
    pass 

# Close the server 
server.close() 
loop.run_until_complete(server.wait_closed()) 
loop.close() 

Jak bym go o uzyskanie formularz wejścia stdin po stronie serwera i określić, które połączenia, aby wysłać go do wejścia, a jednocześnie otrzymał od podłączonych klientów?

Odpowiedz

9

Można użyć harmonogramu loop.add_reader wywołania zwrotnego do uruchomienia, gdy dane są dostępne na sys.stdin, a następnie użyć asyncio.Queue przekazać dane stdin otrzymał do swojej metody data_received:

import sys 
import asyncio 


def got_stdin_data(q): 
    asyncio.async(q.put(sys.stdin.readline())) 

class EchoServerClientProtocol(asyncio.Protocol): 
    def connection_made(self, transport): 
     peername = transport.get_extra_info('peername') 
     print('Connection from {}'.format(peername)) 
     self.transport = transport 

    def data_received(self, data): 
     message = data.decode() 
     print('Data received: {!r}'.format(message)) 
     fut = asyncio.async(q.get()) 
     fut.add_done_callback(self.write_reply) 

    def write_reply(self, fut): 
     reply = fut.result() 
     print('Send: {!r}'.format(reply)) 
     self.transport.write(reply.encode()) 

     #print('Close the client socket') 
     #self.transport.close() 

q = asyncio.Queue() 
loop = asyncio.get_event_loop() 
loop.add_reader(sys.stdin, got_stdin_data, q) 
# Each client connection will create a new protocol instance 
coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888) 
server = loop.run_until_complete(coro) 

# Serve requests until CTRL+c is pressed 
print('Serving on {}'.format(server.sockets[0].getsockname())) 
try: 
    loop.run_forever() 
except KeyboardInterrupt: 
    pass 

# Close the server 
server.close() 
loop.run_until_complete(server.wait_closed()) 
loop.close() 

Jedynym trudne bit jest jak my wywołaj metody Queue.put/Queue.get; Oba są coroutines, których nie można nazwać za pomocą yield from w wywołania zwrotnego lub metod instancji Protocol. Zamiast tego, po prostu planujemy je za pomocą pętli zdarzeń za pomocą asyncio.async, a następnie używamy metody add_done_callback do obsługi odpowiedzi, którą pobieramy z połączenia get().

+0

Podejrzewałem, że potrzebuję użyć kolejki podobnej do tej, ale nie byłem pewien, jak ją wdrożyć. Dziękuję za wspaniałą odpowiedź! Działa ładnie. – RG5

Powiązane problemy