# 11.24 `asynchat` -- 非同期ソケット コマンド/レスポンス ハンドラ

`asynchat`を使うと、`asyncore`を基盤とした非同期な サーバ・クライアントをより簡単に開発する事ができます。 `asynchat`では、プロトコルの要素が任意の文字列で終了するか、ま たは可変長の文字列であるようなプロトコルを容易に制御できるようになってい ます。`asynchat`は、抽象クラス`async_chat`を定義してお り、`async_chat`を継承して`collect_incoming_data()`メソッド と`found_terminator()`メソッドを実装すれば使うことができます。 `async_chat`と`asyncore`は同じ非同期ループを使用してお り、`asyncore.dispatcher`も`asynchat.async_chat`も同じチャネ ルマップに登録する事ができます。通常、`asyncore.dispatcher`はサー バチャネルとして使用し、リクエストの受け付け時に `asynchat.async_chat`オブジェクトを生成します。

class `async_chat`( | )

  このクラスは、`asyncore.dispatcher`から継承した抽象クラスです。 使用する際には`async_chat`のサブクラスを作成し、 `collect_incoming_data()`と`found_terminator()`を定義し なければなりません。`asyncore.dispatcher`のメソッドを使用する事 もできますが、メッセージ/レスポンス処理を中心に行う場合には使えないメ ソッドもあります。

  `asyncore.dispatcher`と同様に、`async_chat`も `select()`呼出し後のソケットの状態からイベントを生成します。 ポーリングループ開始後、イベント処理フレームワークが自動的に `async_chat`のメソッドを呼び出しますので、プログラマが処理を記述 する必要はありません。

  `asyncore.dispatcher`と違い、`async_chat`では プロデューサの first-in-first-outキュー(fifo)を作成する事ができ ます。プロデューサは`more()`メソッドを必ず持ち、このメソッドで チャネル上に送出するデータを返します。プロデューサが枯渇状態 (i.e. これ以上のデータを持たない状態)にある場合、 `more()`は空文字列を返します。この時、`async_chat`は枯渇 状態にあるプロデューサをfifoから除去し、次のプロデューサが存在すればそ のプロデューサを使用します。fifoにプロデューサが存在しない場合、 `handle_write()`は何もしません。リモート端点からの入力の終了や 重要な中断点を検出する場合は、`set_terminator()`に記述します。

  `async_chat`のサブクラスでは、入力メソッド `collect_incoming_data()`と`found_terminator()`を定義 し、チャネルが非同期に受信するデータを処理します。以下にメソッドの解説 を示します。

`close_when_done`( | )

  プロデューサfifoのトップに`None`をプッシュします。このプロデュー サがポップされると、チャネルがクローズします。

`collect_incoming_data`( | data)

  チャネルが受信した不定長のデータをdataに指定して呼び出されます。 このメソッドは必ずオーバライドする必要があり、デフォルトの実装では、 `NotImplementedError` 例外を送出します。

`discard_buffers`( | )

  非常用のメソッドで、全ての入出力バッファとプロデューサfifoを廃棄します。

`found_terminator`( | )

  入力データストリームが、`set_terminator`で指定した終了条件と一 致した場合に呼び出されます。このメソッドは必ずオーバライドする必要があ り、デフォルトの実装では、`NotImplementedError` 例外を送出し ます。入力データを参照する必要がある場合でも引数としては与えられないた め、入力バッファをインスタンス属性として参照しなければなりません。

`get_terminator`( | )

  現在のチャネルの終了条件を返します。

`handle_close`( | )

  チャネル閉じた時に呼び出されます。デフォルトの実装では単にチャネルのソ ケットをクローズします。

`handle_read`( | )

  チャネルの非同期ループでreadイベントが発生した時に呼び出され、デフォル トの実装では、`set_terminator()`で設定された終了条件をチェック します。終了条件として、特定の文字列か受信文字数を指定する事ができま す。終了条件が満たされている場合、`handle_read`は終了条件が成立 するよりも前のデータを引数として`collect_incoming_data()`を呼び 出し、その後`found_terminator()`を呼び出します。

`handle_write`( | )

  アプリケーションがデータを出力する時に呼び出され、デフォルトの実装では `initiate_send()`を呼び出します。`initiate_send()`では `refill_buffer()`を呼び出し、チャネルのプロデューサfifoからデー タを取得します。

`push`( | data)

  dataを持つ`simple_producer`(後述)オブジェクトを生成し、チ ャネルの`producer_fifo`にプッシュして転送します。データをチャネル に書き出すために必要なのはこれだけですが、データの暗号化やチャンク化な どを行う場合には独自のプロデューサを使用する事もできます。

`push_with_producer`( | producer)

  指定したプロデューサオブジェクトをチャネルのfifoに追加します。これより 前にpushされたプロデューサが全て枯渇した後、チャネルはこのプロデューサ から`more()`メソッドでデータを取得し、リモート端点に送信しま す。

`readable`( | )

  `select()`ループでこのチャネルの読み込み可能チェックを行う場 合には、`True`を返します。

`refill_buffer`( | )

  fifoの先頭にあるプロデューサの`more()`メソッドを呼び出し、出力 バッファを補充します。先頭のプロデューサが枯渇状態の場合にはfifoからポ ップされ、その次のプロデューサがアクティブになります。アクティブなプロ デューサが`None`になると、チャネルはクローズされます。

`set_terminator`( | term)

  チャネルで検出する終了条件を設定します。`term`は入力プロトコルデー タの処理方式によって以下の3つの型の何れかを指定します。

  term | 説明
  --- | ---
  string | 入力ストリーム中でstringが検出された時、 `found_terminator()`を呼び出します。
  integer | 指定された文字数が読み込まれた時、 `found_terminator()`を呼び出します。
  `None` | 永久にデータを読み込みます。

  終了条件が成立しても、その後に続くデータは、 `found_terminator()`の呼出し後に再びチャネルを読み込めば取得す る事ができます。

`writable`( | )

  Should return `True` as long as items remain on the producer fifo, or the channel is connected and the channel's output buffer is non-empty.

  プロデューサfifoが空ではないか、チャネルが接続中で出力バッファが空でな い場合、`True`を返します。