\section{\module{asyncore} ---
         非同期ソケットハンドラ}

\declaremodule{builtin}{asyncore}
\modulesynopsis{非同期なソケット制御サービスのためのベースクラス}
\moduleauthor{Sam Rushing}{rushing@nightmare.com}
\sectionauthor{Christopher Petrilli}{petrilli@amber.org}
\sectionauthor{Steve Holden}{sholden@holdenweb.com}
% Heavily adapted from original documentation by Sam Rushing.

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

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

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

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

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


\begin{funcdesc}{loop}{\optional{timeout\optional{, use_poll\optional{,
                       map\optional{,count}}}}}
  ポーリングループを開始し、count回が過ぎるか、全てのオープン済みチャ
  ネルがクローズされた場合のみ終了します。全ての引数はオプションです。
  引数\var{count}のデフォルト値はNoneで、ループは全てのチャネルがクロー
  ズされた場合のみ終了します。 引数\var{timeout}は
  \function{select()}または\function{poll()}の引数timeoutとして渡され、
  秒単位で指定します。デフォルト値は30秒です。引数\var{use_poll}が真のと
  き、\function{select()}ではなく\function{poll()}が使われます。デフォル
  ト値は\code{False}です。

  引数\var{map}には、監視するチャネルをアイテム
  として格納した辞書を指定します。\var{map}が省略された場合、グローバル
  なマップが使用されます。

\end{funcdesc}

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

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

  \begin{datadesc}{ac_in_buffer_size}
  非同期入力バッファのサイズ(デフォルト \code{4096})
  \end{datadesc}

  \begin{datadesc}{ac_out_buffer_size}
  非同期出力バッファのサイズ(デフォルト \code{4096})
  \end{datadesc}

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

  \begin{tableii}{l|l}{code}{イベント}{解説}
    \lineii{handle_connect()}{最初にwriteイベントが発生した時}
    \lineii{handle_close()}{読み込み可能なデータなしでreadイベントが発生
    した時}
    \lineii{handle_accept()}{listen中のソケットでreadイベントが発生した時}
  \end{tableii}

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

\end{classdesc}

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

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

\begin{methoddesc}{handle_write}{}
  非同期ループで、書き込み可能ソケットが実際に書き込み可能になった時に呼
  び出される。このメソッドは、パフォーマンスの向上のためバッファリングを
  行う場合などに利用できます。例：
\begin{verbatim}
def handle_write(self):
    sent = self.send(self.buffer)
    self.buffer = self.buffer[sent:]
\end{verbatim}
\end{methoddesc}

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

\begin{methoddesc}{handle_connect}{}
  % ちょっと手抜き...
  ソケットの接続が確立した時に呼び出されます。``welcome''バナーの送信、プ
  ロトコルネゴシエーションの初期化などを行います。
\end{methoddesc}

\begin{methoddesc}{handle_close}{}
  ソケットが閉じた時に呼び出されます。
\end{methoddesc}

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

\begin{methoddesc}{handle_accept}{}
  % 手抜き...
  listen中のチャネルがリモートホストからの\method{connect()}で接続され、
  接続が確立した時に呼び出されます。
\end{methoddesc}

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

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

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

\begin{methoddesc}{create_socket}{family, type}
  引数も含め、通常のソケット生成と同じ。\refmodule{socket}モジュールを参
  照のこと。
\end{methoddesc}

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

\begin{methoddesc}{send}{data}
  リモート側の端点に\var{data}を送出します。
\end{methoddesc}

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

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

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

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

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


\subsection{asyncoreの例：簡単なHTTPクライアント \label{asyncore-example}}
基本的なサンプルとして、以下に非常に単純なHTTPクライアントを示します。こ
のHTTPクライアントは\class{dispatcher}クラスでソケットを利用しています。

\begin{verbatim}
import asyncore, socket

class http_client(asyncore.dispatcher):

    def __init__(self, host, path):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect( (host, 80) )
        self.buffer = 'GET %s HTTP/1.0\r\n\r\n' % path
        
    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:]

c = http_client('www.python.org', '/')

asyncore.loop()
\end{verbatim}
