python の asyncio について調べる その2
前回 は asyncio event loop がファイルディスクリプタを扱う辺りの API を見た。
今回はその上に構築された API, transport と protocol を調べる。
Transport と protocol
Transport は TCP や SSL などの、何かしらのデータチャンネルを表す。
Protocol は HTTP や SMTP など、 transport 上を流れるデータをしきたりに則って解釈し、意味のあるデータを取り出したり意味のあるデータを送り付けたりする。
Transport は作成時に指定された protocol と結び付けられる。 Transport が受け取ったデータは protocol に渡され、また、 protocol が書き出したいデータは transport に渡される事になる。
Transport についてもう少し
Asyncio は標準で TCP や SSL といった transport を提供している。 とりあえず TCP な transport の動作について、もう少し見てみる。
Transport は作成時に、
自身を作成した event loop
socket
紐付けられた protocol
を受け取る。
そして、 event loop の add_reader()
メソッドを使い、 socket が読みだし可能になったら (データを受け取ったら) 自身の _read_ready()
メソッドが呼び出されるよう登録する。
Event loop から _read_ready()
呼びだされたら、 socket からデータを読みだし、 自身に紐付いた protocol の data_received()
メソッドにデータを渡す。
Protocol より transport にデータが書き込まれた (write()
メソッドが呼ばれた) 際は、 event loop の add_writer()
メソッドで socket が書き込み可能になるのを待ち、 呼ばれた callback
内でたまったデータを書き出してゆく。
試す
前回同様、サーバーから返ってきたデータを画面に出すクライアントを書いてみる。
import asyncio import time class HelloProtocol(asyncio.Protocol): def connection_made(self, transport): print('connected', transport) def data_received(self, data): # Transport により、 socket からデータが読み出されるたびに呼び出される # 前回同様、現在時間と受け取ったデータを画面に表示する。 print(int(time.time())) print(data) print('end') def eof_received(self): print('eof') # connection が切断されたら event loop を停止。 loop.stop() loop = asyncio.get_event_loop() # XXX: おまじないとして ensure_future を呼ぶ。 asyncio.ensure_future( # 名前解決、 socket 接続、 TCP 用の transport インスタンス作成、 # protocol インスタンス作成、 transport と protocol のひも付け、 # 辺りまでやってくれる event loop のメソッド呼び出し。 loop.create_connection(HelloProtocol, '127.0.0.1', 9999)) loop.run_forever()
実行してみる:
$ python3.5 hello-client3.py
connected <_SelectorSocketTransport fd=7 read=idle write=<idle, bufsize=0>>
1465115330
b'hello world\n'
end
1465115331
b'hello world\n'
end
1465115332
b'hello world\n'
end
eof
前回同様、1秒毎 (データ受信ごと) にコールバックが呼び出されていることが確認できる。