2017-08-26 11 views
25

私は読み取りが含まれているとasyncio接続のストリームを書き込むために使用されConnectionオブジェクトがあります。サーバー側でストリームから取得する正しい方法は何ですか?

class Connection(object): 

    def __init__(self, stream_in, stream_out): 
     object.__init__(self) 

     self.__in = stream_in 
     self.__out = stream_out 

    def read(self, n_bytes : int = -1): 
     return self.__in.read(n_bytes) 

    def write(self, bytes_ : bytes): 
     self.__out.write(bytes_) 
     yield from self.__out.drain() 

connectedConnectionオブジェクトにクライアントが接続するたびに作成されますが、その後、4つのバイトを読み込みます。

@asyncio.coroutine 
def new_conection(stream_in, stream_out): 
    conn = Connection(stream_in, stream_out) 
    data = yield from conn.read(4) 
    print(data) 

そして、クライアント側では、4バイトが書き出されます。

@asyncio.coroutine 
def client(loop): 
    ... 
    conn = Connection(stream_in, stream_out) 
    yield from conn.write(b'test') 

は、これはほとんど期待が、私はyield fromすべてのreadwriteコールに持っている作品として。私は複数の場所からreadwriteを呼び出す場合、私がいないことを好むだろう

def read(self, n_bytes : int = -1): 
    data = yield from self.__in.read(n_bytes) 
    return data 

むしろデータを取得するよりも、私は

<generator object StreamReader.read at 0x1109983b8> 

のような出力が得られます。私はConnection内部からINGのyield fromを試してみました毎回yield fromを繰り返します。むしろそれらをConnectionの内部に保ちます。私の究極の目標はこれに私のnew_conection機能を削減します

@asyncio.coroutine 
def new_conection(stream_in, stream_out): 
    conn = Connection(stream_in, stream_out) 
    print(conn.read(4)) 
+0

なぜあなたは降伏する必要がありますか? conn.read(4)から返さなければ、バイトオブジェクトを返すように見えます。それはあなたがここで探しているものですか? – RageCage

+0

@RageCage:収率from'ing 'なし' conn.read(4) '依然として発生返し:私は明らかにしておく必要があり、<0x1019262b0でジェネレータオブジェクトConnection.readを>' ' –

+0

申し訳ありません。 conn.read()の最初の反復(1行バージョン)から結果が得られない場合、結果は何ですか? – RageCage

答えて

6

StreamReader.read is a coroutineので、それを呼び出すためのあなたの唯一のオプションは、a)TaskまたはFutureでそれをラップし、イベントループを介して、B)awaitasync defで定義されたコルーチンからそれをINGのことを実行している、またはc)からでyield fromを使用してコルーチンは、@asyncio.coroutineで装飾された関数として定義されています。 event loops can't be started while they're already runningConnection.read以来

はイベントループから呼び出される(コルーチンnew_connection経由)、あなたはStreamReader.readためTaskまたはFutureを実行するには、そのイベントループを再利用することはできません。 stop the event loop(悲惨で、おそらく正しくはできません)またはcreate a new event loop(コルーチンの使用目的が乱雑で敗北している)のいずれかです。どちらも望ましくないので、Connection.readはコルーチンまたはasync関数である必要があります。

他の2つのオプション(@asyncio.coroutine -decorated関数でasync defコルーチンにawaitまたはyield from)はほとんど同等です。唯一の違いは、yield from@asyncio.coroutineを使用する唯一のオプション(コルーチンとasyncio前3.4に存在していなかったので、他のバージョンは無関係である)であり、3.4のために、async def and await were added in Python 3.5ことです。 async defでコルーチンを定義することデコレータでよりクリーンかつ明確であるので、個人的に、私は、async defawaitを使用して好みます。簡単に言うと

Connection.readnew_connectionはコルーチン(デコレータまたはasyncキーワードのいずれかを使用して)ことがあり、他のコルーチン(new_connectionawait conn.read(4)、およびConnection.readawait self.__in.read(n_bytes))を呼び出すときawait(またはyield from)を使用します。

+1

ああ、とても素敵な答えMego!これは、彼らが何を話しているかを知っている誰かによって明確に書かれています。私はそれを読んで多くを学んだ。 +1 –

1

私はライン620上StreamReader source codeの塊を発見し、実際に機能の使用方法の完璧な例です。

私の前の答えでは、​​はコルーチン(これは、asyncioのモジュールXDだと考えていたはずですが)の結果が得られていることを見落としました。それは実際に発電機であり、あなたはそれから降伏する必要があります。

ソースコードからこのループを借りて、あなたの読み取り機能は、次のようになります。発電機がある

def read(self, n_bytes : int = -1): 
    data = bytearray() #or whatever object you are looking for 
    while 1: 
     block = yield from self.__in.read(n_bytes) 
     if not block: 
      break 
     data += block 
    return data 

​​ので、あなたはそれを知らせるために、空の結果が得られまで、そこから得るために続けなければなりません読書の終わり。これで、読込み関数はジェネレータではなくデータを返すようになりました。あなたはこのバージョンのconn.read()から降伏する必要はありません。

+0

あなたが提供したのとまったく同じ関数を使用して、私はまだジェネレータオブジェクト( 'Connection.read')を受け取っています。 –

+0

あなたはまだconn.read呼び出しから降伏していますか?データを印刷し、読み込み関数に(データ)を入力して、返す前にそれを確認してください。 – RageCage

+0

いいえ、私はそれを削除し、代わりに 'data = conn.read(4)'を試しました。それはジェネレータです。 –

関連する問題