asynchat —非同期ソケットコマンド/応答ハンドラー
ソースコード: :source: `Lib / asynchat.py`
バージョン3.6以降非推奨:代わりに asyncio を使用してください。
このモジュールは、 asyncore インフラストラクチャ上に構築されており、非同期クライアントとサーバーを簡素化し、要素が任意の文字列で終了するプロトコルや可変長のプロトコルの処理を容易にします。 asynchat は、サブクラス化する抽象クラス async_chat を定義し、collect_incoming_data()
およびfound_terminator()
メソッドの実装を提供します。 asyncore と同じ非同期ループを使用し、 asyncore.dispatcher と asynchat.async_chat の2種類のチャネルをチャネルマップに自由に混在させることができます。 。 通常、 asyncore.dispatcher サーバーチャネルは、着信接続要求を受信すると、新しい asynchat.async_chat チャネルオブジェクトを生成します。
- class asynchat.async_chat
このクラスは、 asyncore.dispatcher の抽象サブクラスです。 コードを実際に使用するには、 async_chat をサブクラス化して、意味のある collect_incoming_data()および found_terminator()メソッドを提供する必要があります。 asyncore.dispatcher メソッドを使用できますが、メッセージ/応答のコンテキストですべてが意味をなすわけではありません。
asyncore.dispatcher と同様に、 async_chat は、
select()
呼び出し後のソケット状態の分析によって生成される一連のイベントを定義します。 ポーリングループが開始されると、 async_chat オブジェクトのメソッドは、プログラマー側のアクションなしでイベント処理フレームワークによって呼び出されます。2つのクラス属性を変更して、パフォーマンスを向上させたり、場合によってはメモリを節約したりすることもできます。
- ac_in_buffer_size
非同期入力バッファサイズ(デフォルトは
4096
)。
- ac_out_buffer_size
非同期出力バッファサイズ(デフォルトは
4096
)。
asyncore.dispatcher とは異なり、 async_chat では、プロデューサーの FIFO キューを定義できます。 プロデューサーは、
more()
という1つのメソッドのみを必要とします。このメソッドは、チャネルで送信されるデータを返す必要があります。 プロデューサーは、more()
メソッドに空のバイトオブジェクトを返すようにすることで、枯渇( ie にデータが含まれていないこと)を示します。 この時点で、 async_chat オブジェクトはキューからプロデューサーを削除し、次のプロデューサーがある場合はその使用を開始します。 プロデューサーキューが空の場合、handle_write()
メソッドは何もしません。 チャネルオブジェクトの set_terminator()メソッドを使用して、リモートエンドポイントからの着信送信の終了または重要なブレークポイントを認識する方法を記述します。機能する async_chat サブクラスを構築するには、入力メソッド collect_incoming_data()および found_terminator()は、チャネルが非同期で受信するデータを処理する必要があります。 その方法を以下に説明します。
- async_chat.close_when_done()
None
をプロデューサーキューにプッシュします。 このプロデューサーがキューからポップされると、チャネルが閉じられます。
- async_chat.collect_incoming_data(data)
- 任意の量の受信データを保持する data で呼び出されます。 オーバーライドする必要があるデフォルトのメソッドでは、 NotImplementedError 例外が発生します。
- async_chat.discard_buffers()
- 緊急時には、このメソッドは入力および/または出力バッファーとプロデューサーキューに保持されているデータを破棄します。
- async_chat.found_terminator()
- 着信データストリームが set_terminator()によって設定された終了条件に一致したときに呼び出されます。 オーバーライドする必要があるデフォルトのメソッドでは、 NotImplementedError 例外が発生します。 バッファリングされた入力データは、インスタンス属性を介して利用できる必要があります。
- async_chat.get_terminator()
- チャネルの現在のターミネータを返します。
- async_chat.push(data)
- データをチャネルのキューにプッシュして、確実に送信します。 チャネルにデータをネットワークに書き出すために必要なのはこれだけですが、たとえば、暗号化やチャンク化を実装するために、より複雑なスキームで独自のプロデューサーを使用することもできます。
- async_chat.push_with_producer(producer)
- プロデューサーオブジェクトを取得し、チャネルに関連付けられたプロデューサーキューに追加します。 現在プッシュされているすべてのプロデューサーが使い果たされると、チャネルは
more()
メソッドを呼び出してこのプロデューサーのデータを消費し、データをリモートエンドポイントに送信します。
- async_chat.set_terminator(term)
チャネルで認識される終了条件を設定します。
term
は、着信プロトコルデータを処理する3つの異なる方法に対応する、3つのタイプの値のいずれかです。期間
説明
ストリング
文字列が入力ストリームで見つかったときに found_terminator()を呼び出します
整数
指定された文字数を受信すると、 found_terminator()を呼び出します
None
チャネルはデータを永久に収集し続けます
found_terminator()が呼び出された後、ターミネータに続くデータはすべてチャネルで読み取ることができることに注意してください。
asynchatの例
次の部分的な例は、 async_chat を使用してHTTPリクエストを読み取る方法を示しています。 Webサーバーは、着信クライアント接続ごとにhttp_request_handler
オブジェクトを作成する場合があります。 最初に、チャネルターミネータがHTTPヘッダーの最後の空白行と一致するように設定されており、フラグがヘッダーが読み取られていることを示していることに注意してください。
ヘッダーが読み取られると、リクエストのタイプがPOST(入力ストリームにさらにデータが存在することを示す)の場合、Content-Length:
ヘッダーを使用して、適切な量のデータを読み取るための数値ターミネーターを設定します。チャネル。
handle_request()
メソッドは、チャネルターミネーターをNone
に設定して、Webクライアントから送信された無関係なデータが無視されるようにした後、関連するすべての入力がマーシャリングされると呼び出されます。
import asynchat
class http_request_handler(asynchat.async_chat):
def __init__(self, sock, addr, sessions, log):
asynchat.async_chat.__init__(self, sock=sock)
self.addr = addr
self.sessions = sessions
self.ibuffer = []
self.obuffer = b""
self.set_terminator(b"\r\n\r\n")
self.reading_headers = True
self.handling = False
self.cgi_data = None
self.log = log
def collect_incoming_data(self, data):
"""Buffer the data"""
self.ibuffer.append(data)
def found_terminator(self):
if self.reading_headers:
self.reading_headers = False
self.parse_headers(b"".join(self.ibuffer))
self.ibuffer = []
if self.op.upper() == b"POST":
clen = self.headers.getheader("content-length")
self.set_terminator(int(clen))
else:
self.handling = True
self.set_terminator(None)
self.handle_request()
elif not self.handling:
self.set_terminator(None) # browsers sometimes over-send
self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
self.handling = True
self.ibuffer = []
self.handle_request()