18.6。 非同期 —非同期ソケットハンドラー
ソースコード: :source: `Lib / asyncore.py`
バージョン3.6以降非推奨:代わりに asyncio を使用してください。
このモジュールは、非同期ソケットサービスクライアントおよびサーバーを作成するための基本的なインフラストラクチャを提供します。
単一のプロセッサ上のプログラムに「一度に複数のこと」を実行させる方法は2つしかありません。 マルチスレッドプログラミングはそれを行うための最も簡単で最も一般的な方法ですが、実際に複数のスレッドを使用せずにマルチスレッドのほぼすべての利点を利用できる、別の非常に異なる手法があります。 プログラムが主にI / Oバウンドである場合にのみ、実際に実用的です。 プログラムがプロセッサにバインドされている場合は、プリエンプティブなスケジュールされたスレッドがおそらく本当に必要なものです。 ただし、ネットワークサーバーがプロセッサにバインドされることはめったにありません。
オペレーティングシステムがI / Oライブラリでselect()
システムコールをサポートしている場合(ほとんどすべてがサポートしている場合)、それを使用して複数の通信チャネルを一度に調整できます。 I / Oが「バックグラウンド」で行われている間に他の作業を行う。 この戦略は、特に最初は奇妙で複雑に見えるかもしれませんが、マルチスレッドプログラミングよりも多くの点で理解と制御が簡単です。 asyncore モジュールは、困難な問題の多くを解決し、洗練された高性能ネットワークサーバーとクライアントを構築するタスクを簡単にします。 「会話型」のアプリケーションとプロトコルの場合、コンパニオン asynchat モジュールは非常に貴重です。
両方のモジュールの背後にある基本的な考え方は、1つ以上のネットワークチャネル、クラス asyncore.dispatcher および asynchat.async_chat のインスタンスを作成することです。 チャネルを作成すると、それらがグローバルマップに追加されます。独自のマップを提供しない場合は、 loop()関数で使用されます。
最初のチャネルが作成されると、 loop()関数を呼び出すと、チャネルサービスがアクティブになります。これは、最後のチャネル(非同期サービス中にマップに追加されたチャネルを含む)が作成されるまで続きます。閉まっている。
- asyncore.loop([timeout[, use_poll[, map[, count]]]])
カウントが通過するか、開いているすべてのチャネルが閉じられた後に終了するポーリングループに入ります。 すべての引数はオプションです。 count パラメーターのデフォルトは
None
であるため、すべてのチャネルが閉じられた場合にのみループが終了します。 timeout 引数は、適切な select()または poll()呼び出しのタイムアウトパラメーターを秒単位で設定します。 デフォルトは30秒です。 use_poll パラメーターがtrueの場合、 poll()を select()よりも優先して使用する必要があることを示します(デフォルトはFalse
) 。map パラメータは、アイテムが監視するチャネルである辞書です。 チャネルが閉じられると、マップから削除されます。 map を省略すると、グローバルマップが使用されます。 チャネル( asyncore.dispatcher 、 asynchat.async_chat およびそのサブクラスのインスタンス)は、マップ内で自由に混在させることができます。
- class asyncore.dispatcher
dispatcher クラスは、低レベルのソケットオブジェクトの薄いラッパーです。 それをより便利にするために、非同期ループから呼び出されるイベント処理のためのいくつかのメソッドがあります。 それ以外の場合は、通常の非ブロッキングソケットオブジェクトとして扱うことができます。
特定の時間または特定の接続状態で低レベルのイベントが発生すると、非同期ループに特定の高レベルのイベントが発生したことが通知されます。 たとえば、別のホストに接続するためのソケットを要求した場合、ソケットが初めて書き込み可能になったときに接続が確立されたことがわかります(この時点で、成功を期待してソケットに書き込むことができます。 )。 暗黙の上位レベルのイベントは次のとおりです。
イベント
説明
handle_connect()
最初の読み取りまたは書き込みイベントによって暗示されます
handle_close()
利用可能なデータがない読み取りイベントによって暗示されます
handle_accepted()
リスニングソケットの読み取りイベントによって暗示されます
非同期処理中に、マップされた各チャネルの読み取り可能()および書き込み可能()メソッドを使用して、チャネルのソケットをチャネルのリストに追加する必要があるかどうかを判断します
select()
edまたは、読み取りおよび書き込みイベントの場合はpoll()
ed。したがって、チャネルイベントのセットは、基本的なソケットイベントよりも大きくなります。 サブクラスでオーバーライドできるメソッドの完全なセットは次のとおりです。
- handle_read()
非同期ループが、チャネルのソケットでの
read()
呼び出しが成功することを検出したときに呼び出されます。
- handle_write()
非同期ループが書き込み可能なソケットを書き込むことができることを検出したときに呼び出されます。 多くの場合、このメソッドはパフォーマンスに必要なバッファリングを実装します。 例えば:
def handle_write(self): sent = self.send(self.buffer) self.buffer = self.buffer[sent:]
- handle_expt()
ソケット接続の帯域外(OOB)データがある場合に呼び出されます。 OOBはサポートが不十分で、ほとんど使用されないため、これはほとんど発生しません。
- handle_connect()
アクティブなオープナーのソケットが実際に接続したときに呼び出されます。 たとえば、「ウェルカム」バナーを送信したり、リモートエンドポイントとのプロトコルネゴシエーションを開始したりする場合があります。
- handle_close()
ソケットが閉じているときに呼び出されます。
- handle_error()
例外が発生し、他の方法で処理されない場合に呼び出されます。 デフォルトバージョンは、要約されたトレースバックを出力します。
- handle_accept()
ローカルエンドポイントに対して connect()呼び出しを発行した新しいリモートエンドポイントとの接続を確立できる場合に、リスニングチャネル(パッシブオープナー)で呼び出されます。 バージョン3.2では非推奨。 代わりに handle_accepted()を使用してください。
バージョン3.2以降非推奨。
- handle_accepted(sock, addr)
ローカルエンドポイントに対して connect()呼び出しを発行した新しいリモートエンドポイントとの接続が確立されたときに、リスニングチャネル(パッシブオープナー)で呼び出されます。 sock は、接続でデータを送受信するために使用できる new ソケットオブジェクトであり、 addr は、接続のもう一方の端のソケットにバインドされたアドレスです。 。
バージョン3.2の新機能。
- readable()
非同期ループの前後で毎回呼び出され、読み取りイベントが発生する可能性のあるリストにチャネルのソケットを追加する必要があるかどうかを判断します。 デフォルトのメソッドは単に
True
を返します。これは、デフォルトでは、すべてのチャネルが読み取りイベントに関心があることを示しています。
- writable()
非同期ループの前後で毎回呼び出され、書き込みイベントが発生する可能性のあるリストにチャネルのソケットを追加する必要があるかどうかを判断します。 デフォルトのメソッドは単に
True
を返します。これは、デフォルトでは、すべてのチャネルが書き込みイベントに関心があることを示しています。
さらに、各チャネルは多くのソケットメソッドを委任または拡張します。 これらのほとんどは、ソケットパートナーとほぼ同じです。
- create_socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
これは通常のソケットの作成と同じであり、作成に同じオプションを使用します。 ソケットの作成については、 socket のドキュメントを参照してください。
バージョン3.3で変更: family および type 引数は省略できます。
- connect(address)
通常のソケットオブジェクトと同様に、 address は、ホストが接続する最初の要素と、2番目のポート番号を持つタプルです。
- send(data)
データをソケットのリモートエンドポイントに送信します。
- recv(buffer_size)
ソケットのリモートエンドポイントから最大 buffer_size バイトを読み取ります。 空のbytesオブジェクトは、チャネルがもう一方の端から閉じられていることを意味します。
select.select()または select.poll()がソケットを報告した場合でも、 recv()は BlockingIOError を発生させる可能性があることに注意してください読む準備ができました。
- listen(backlog)
ソケットへの接続をリッスンします。 backlog 引数は、キューに入れられた接続の最大数を指定し、少なくとも1である必要があります。 最大値はシステムに依存します(通常は5)。
- bind(address)
ソケットをアドレスにバインドします。 ソケットはまだバインドされていない必要があります。 ( address の形式は、アドレスファミリによって異なります。詳細については、 socket のドキュメントを参照してください。)ソケットを再利用可能としてマークするには(
SO_REUSEADDR
を設定します)オプション)、 dispatcher オブジェクトのset_reuse_addr()
メソッドを呼び出します。
- accept()
接続を受け入れます。 ソケットはアドレスにバインドされ、接続をリッスンする必要があります。 戻り値は、
None
またはペア(conn, address)
のいずれかです。ここで、 conn は、接続でデータを送受信するために使用できる new ソケットオブジェクトです。 address は、接続のもう一方の端にあるソケットにバインドされているアドレスです。None
が返される場合は、接続が行われなかったことを意味します。この場合、サーバーはこのイベントを無視し、さらに着信接続をリッスンし続ける必要があります。
- close()
ソケットを閉じます。 ソケットオブジェクトに対する今後のすべての操作は失敗します。 リモートエンドポイントは、(キューに入れられたデータがフラッシュされた後)それ以上データを受信しません。 ソケットは、ガベージコレクションされると自動的に閉じられます。
- class asyncore.dispatcher_with_send
- ディスパッチャーサブクラス。単純なバッファー出力機能を追加し、単純なクライアントに役立ちます。 より洗練された使用法については、 asynchat.async_chat を使用してください。
- class asyncore.file_dispatcher
- file_dispatcherは、オプションのマップ引数とともにファイル記述子またはファイルオブジェクトを受け取り、
poll()
またはloop()
関数で使用できるようにラップします。 ファイルオブジェクトまたはfileno()
メソッドが提供されている場合、そのメソッドが呼び出され、 file_wrapper コンストラクターに渡されます。 可用性:UNIX。
- class asyncore.file_wrapper
- file_wrapperは整数のファイル記述子を受け取り、 os.dup()を呼び出してハンドルを複製し、file_wrapperとは独立して元のハンドルを閉じることができるようにします。 このクラスは、 file_dispatcher クラスで使用するソケットをエミュレートするのに十分なメソッドを実装します。 可用性:UNIX。
18.6.1。 asyncore基本HTTPクライアントの例
これは、 dispatcher クラスを使用してソケット処理を実装する非常に基本的なHTTPクライアントです。
import asyncore
class HTTPClient(asyncore.dispatcher):
def __init__(self, host, path):
asyncore.dispatcher.__init__(self)
self.create_socket()
self.connect( (host, 80) )
self.buffer = bytes('GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' %
(path, host), 'ascii')
def handle_connect(self):
pass
def handle_close(self):
self.close()
def handle_read(self):
print(self.recv(8192))
def writable(self):
return (len(self.buffer) > 0)
def handle_write(self):
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
client = HTTPClient('www.python.org', '/')
asyncore.loop()
18.6.2。 asyncore基本的なエコーサーバーの例
dispatcher クラスを使用して接続を受け入れ、着信接続をハンドラーにディスパッチする基本的なエコーサーバーを次に示します。
import asyncore
class EchoHandler(asyncore.dispatcher_with_send):
def handle_read(self):
data = self.recv(8192)
if data:
self.send(data)
class EchoServer(asyncore.dispatcher):
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.create_socket()
self.set_reuse_addr()
self.bind((host, port))
self.listen(5)
def handle_accepted(self, sock, addr):
print('Incoming connection from %s' % repr(addr))
handler = EchoHandler(sock)
server = EchoServer('localhost', 8080)
asyncore.loop()