# 11.23 `asyncore` -- 非同期ソケットハンドラ

このモジュールは、非同期ソケットサービスのクライアント・サーバを開発する ための基盤として使われます。

CPUが一つしかない場合、プログラムが``二つのことを同時に''実行する方法は 二つしかありません。もっとも簡単で一般的なのはマルチスレッドを利用する方 法ですが、これとはまったく異なるテクニックで、一つのスレッドだけでマルチ スレッドと同じような効果を得られるテクニックがあります。このテクニックは I/O処理が中心である場合にのみ有効で、CPU負荷の高いプログラムでは効果が無 く、この場合にはプリエンプティブなスケジューリングが可能なスレッドが有効 でしょう。しかし、多くの場合、ネットワークサーバではCPU負荷よりはIO負荷 が問題となります。

もしOSのI/Oライブラリがシステムコール `select()` をサポートし ている場合（ほとんどの場合はサポートされている）、I/O処理は``バックグラ ウンド''で実行し、その間に他の処理を実行すれば、複数の通信チャネルを同時 にこなすことができます。一見、この戦略は奇妙で複雑に思えるかもしれません が、いろいろな面でマルチスレッドよりも理解しやすく、制御も容易です。 `asyncore`は多くの複雑な問題を解決済みなので、洗練され、パフォー マンスにも優れたネットワークサーバとクライアントを簡単に開発することがで きます。とくに、`asynchat`のような、対話型のアプリケーションや プロトコルには非常に有効でしょう。

基本的には、この二つのモジュールを使う場合は一つ以上のネットワーク チャネルを`asyncore.dispatcher`クラス、または `asynchat.async_chat`のインスタンスとして作成します。作成されたチ ャネルはグローバルマップに登録され、`loop()`関数で参照されま す。`loop()`には、専用のマップを渡す事も可能です。

チャネルを生成後、`loop()`を呼び出すとチャネル処理が開始し、最 後のチャネル（非同期処理中にマップに追加されたチャネルを含む）が閉じるま で継続します。

`loop`( | [timeout[, use_poll[, map]]])

  ポーリングループを開始し、全てのオープン済みチャネルがクローズされた場 合のみ終了します。全ての引数はオプションです。引数timeoutは `select()`または`poll()`の引数timeoutとして渡され、 秒単位で指定します。デフォルト値は30秒です。引数use_pollが真のと き、`select()`ではなく`poll()`が使われます。デフォル ト値は`False`です。引数mapには、監視するチャネルをアイテム として格納した辞書を指定します。mapが省略された場合、グローバル なマップが使用される。グローバルなマップは、チャネルクラスの `__init__()`メソッドが呼び出されたときに自動的に更新されま す。- この仕組みを利用するのであれば、チャネルクラスの `__init__()`はオーバライドするのではなく、拡張しなければなりま せん。

class `dispatcher`( | )

  `dispatcher`クラスは、低レベルソケットオブジェクトの薄いラッ パーです。便宜上、非同期ループから呼び出されるイベント処理メソッドを 追加していますが、これ以外の点では、non-blockingなソケットと同様で す。

  `dispatcher`クラスには二つのクラス属性があり、パフォーマンス向上 やメモリの削減のために更新する事ができます。

  `ac_in_buffer_size`
    非同期入力バッファのサイズ(デフォルト `4096`)

  `ac_out_buffer_size`
    非同期出力バッファのサイズ(デフォルト `4096`)

  非同期ループ内で低レベルイベントが発生した場合、発生のタイミングやコネ クションの状態から特定の高レベルイベントへと置き換えることができます。 例えばソケットを他のホストに接続する場合、最初の書き込み可能イベントが 発生すれば接続が完了した事が分かります(この時点で、ソケットへの書き込 みは成功すると考えられる)。このように判定できる高レベルイベントを以下 に示します：

  イベント | 解説
  --- | ---
  `handle_connect()` | 最初にwriteイベントが発生した時
  `handle_close()` | 読み込み可能なデータなしでreadイベントが発生 した時
  `handle_accept()` | listen中のソケットでreadイベントが発生した時

  非同期処理中、マップに登録されたチャネルの`readable()`メソッド と`writable()`メソッドが呼び出され、`select()`か `poll()`でread/writeイベントを検出するリストに登録するか否か を判定します。

このようにして、チャネルでは低レベルなソケットイベントの種類より多くの種 類のイベントを検出する事ができます。以下にあげるイベントは、サブクラスで オーバライドすることが可能です：

`handle_read`( | )

  非同期ループで、チャネルのソケットの`read()`メソッドの呼び出しが 成功した時に呼び出されます。

`handle_write`( | )

  非同期ループで、書き込み可能ソケットが実際に書き込み可能になった時に呼 び出される。このメソッドは、パフォーマンスの向上のためバッファリングを 行う場合などに利用できます。例：

      def handle_write(self):
          sent = self.send(self.buffer)
          self.buffer = self.buffer[sent:]

`handle_expt`( | )

  out of band (OOB)データが検出された時に呼び出されます。OOBはあまりサポー トされておらず、また滅多に使われないので、`handle_expt()`が呼び 出されることはほとんどありません。

`handle_connect`( | )

  ソケットの接続が確立した時に呼び出されます。``welcome''バナーの送信、プ ロトコルネゴシエーションの初期化などを行います。

`handle_close`( | )

  ソケットが閉じた時に呼び出されます。

`handle_error`( | )

  捕捉されない例外が発生した時に呼び出されます。デフォルトでは、短縮したト レースバック情報が出力されます。

`handle_accept`( | )

  listen中のチャネルがリモートホストからの`connect()`で接続され、 接続が確立した時に呼び出されます。

`readable`( | )

  非同期ループ中に呼び出され、readイベントの監視リストに加えるか否かを決 定します。デフォルトのメソッドでは`True`を返し、readイベントの発 生を監視します。

`writable`( | )

  非同期ループ中に呼び出され、writeイベントの監視リストに加えるか否かを 決定します。デフォルトのメソッドでは`True`を返し、writeイベントの 発生を監視します。

さらに、チャネルにはソケットのメソッドとほぼ同じメソッドがあり、チャネル はソケットのメソッドの多くを委譲・拡張しており、ソケットとほぼ同じメソッ ドを持っています。

`create_socket`( | family, type)

  引数も含め、通常のソケット生成と同じ。`socket`モジュールを参 照のこと。

`connect`( | address)

  通常のソケットオブジェクトと同様、addressには一番目の値が接続先 ホスト、2番目の値がポート番号であるタプルを指定します。

`send`( | data)

  リモート側の端点にdataを送出します。

`recv`( | buffer_size)

  リモート側の端点より、最大buffer_sizeバイトのデータを読み込みま す。長さ0の文字列が返ってきた場合、チャネルはリモートから切断された事 を示します。

`listen`( | backlog)

  ソケットへの接続を待つ。引数backlogは、キューイングできるコネク ションの最大数を指定します(1以上)。最大数はシステムに依存でします（通 常は5)

`bind`( | address)

  ソケットをaddressにバインドします。ソケットはバインド済みであっ てはなりません。(addressの形式は、アドレスファミリに依存します。 `socket`モジュールを参照のこと。)

`accept`( | )

  接続を受け入れます。ソケットはアドレスにバインド済みであり、 `listen()`で接続待ち状態でなければなりません。戻り値は `(conn, address)`のペアで、connはデータの送受信 を行うソケットオブジェクト、addressは接続先ソケットがバインドさ れているアドレスです。

`close`( | )

  ソケットをクローズします。以降の全ての操作は失敗します。リモート端点で は、キューに溜まったデータ以外、これ以降のデータ受信は行えません。ソケ ットはガベージコレクト時に自動的にクローズされます。