Winsock Programmer's FAQ
第7章: 論説記事: CSocket はなぜ有害か?

CSocket はなぜ有害か?

Warren Young 著

もしあなたが MFC ユーザであるなら、おそらく MFC ライブラリの CAsyncSocket クラスやその仲間を使うことを考えているでしょう。し かし残念ながら、その階層構造全体に問題がある、ということを、プロ グラムを書き始める前に知っておくべきでしょう。

CAsyncSocket

CAsyncSocket は非同期ソケットのラッパーの骨組みです。この FAQ では現在、二つの例、「直API版」 「CAsyncSocket ベース版」 クライアントを示しています。

これらの例をダウンロードして眺めてみると、CAsyncSocket 版はあ まりきれいではなく、いくつか問題があることに気が付くでしょう。ま ず一番目に、CAsyncSocket は DNS 参照を同期的に行います。従って DNS サーバが遅いと、プログラムは長時間ブロックしてしまいます。二 番目に、CAsyncSocket クラスと CWnd クラスの両方を継承することは できません。なぜなら、これらのクラス間には基底クラスの衝突があり、 コンパイラが解決できないからです。このため、ネットワークのタイム アウトやその他いくつかのものを設定することができません。これは設 計ミスです。CAsyncSocket は、Winsock からのウィンドウメッセージ を受信するために、CWnd から派生させるべきものです(そうすると Winsow タイマーも使えるようになります)。この対処として、 CAsyncSocket は、メッセージを制御するための別のウィンドウを生成 しています。

CSocket

CSocket により、世の流れは、平穏なものから悪い方に流れていま す。

CSocket は CAsyncSocket のサブクラスであり、非同期ソケットを 使ってブロック型 I/O を「模倣」しています。これは WSAEWOULDBLOCK エラーが返される度に繰り返す小さなメッ セージポンプを実行させることで実現しています。これは技術的には面 白いものですが、副作用として再入可能性に関する問題が発生してしま います。 CSocket が「ブロック中」の間、ウィンドウメッセージを取 り出し続けるので、そのメッセージが引き金となって、ブロック中の CSocket オブジェクトに対して別の呼出しを行ってしまうことがありえ るのです。これと同様の問題、関連する問題として他に何があるかは、 読者への練習問題として残しておきましょう。

また、CSocket には明らかにバグが存在します。CSocket は、重要 な場面においてクラッシュしたりアサーション失敗を引き起こしたりす ることがよくあります。もともとの設計仕様の動作さえも満足にできな いネットワークアプリケーションに使う価値があるなんて、いったい誰 が聞いたことがある?

しかし、CSocket への批判として最も決定的なことは、むしろ本物 のブロック型ソケットの方が使いこなすのが簡単、ということです。 Winsock のブロック型ソケットは、プログラムするのが本当に簡単です。 バグが無くて、動かすのに複雑さが少ないブロック型ソケットがあるの に、なんでわざわざ模造品でバグバグなブロック型ソケットの方を選ぶ 必要があるんでしょうか?

CCESocket

CAsyncSocket と CSocket は Windows CE 上では動作しません。 Windows CE ではバークレースタイルのブロック型・非ブロック型ソケッ トしかサポートしていないからです。しかし非同期通知をあきらめたく はなかったので、Microsoft はまたもや、設計殺しを決定したのです: CCESocket という CSocket(!) のサブクラスを作り、ワーカースレッド とブロック型ソケットの組を使って非同期通知をエミュレートするよう にしたのです。

結論

話をまとめましょう。まず最初に非同期ソケットがありました。そ して、その非同期ソケットの上に、同期ソケットに見せかけるような層 を追加しました。そしてさらにその上に、同期ソケットを使って非同期 ソケットを真似する層を追加したのです。しかも、ここでは基底クラス の擬似同期機構は使わず、今度は本物の Winsock のブロック型ソケッ トを使って、です。この最後の層は、基底クラスの機能のほぼ全てをオー バーライドしなくてはなりませんでした! Redmond の会社への助言: そ ろそろ空っぽのデカ頭達をリストラした方がいいんじゃない? (訳注: Redmond は Microsoft 本社のある都市。)

CSocket は良きオブジェクト指向設計の「A は B の一種である」 ("is a")ルールを破っています。この階層構造では、Microsoft はこう 言っているのです: 「ブロック型ソケットは非同期ソケットの一種であ る。そしてスレッドベースの非同期ソケットもどきはブロック型ソケッ トの一種である。しかし逆が成り立つとは限らない。」 俺、「三ばか 大将」でも見てるのかと思っちまったよ…(訳注: 「三ばか大将」はア メリカのTVコメディ番組。日本でいえば「ドリフ」あたりか)。

この階層構造の中で多少なりとも使えるクラスは唯一 CAsyncSocket です。しかし使える便利であるは違います。もしやり たいことがごく簡単なものであれば、Winsock API をそのまま使って書 くのも同じくらい簡単でしょう。非常に困難なネットワークの問題を解 決しようとしているのであれば、私だったら、単に Winsock をラッピ ングするだけではない、もっと賢いクラスライブラリを使うでしょう。 例えば、TCP ストリームのバッファリングを行い、論理的なパケットに 切り分けてくれる機能を提供するラッパーとか、そういうものです(ライブラリの章にいくつか あります)。これらのリソースを与えられてもなお、あなたは CAsyncSocket を使いたいと思いますか?

こんな設計上の混乱が起きてしまった原因は、Microsoft 設計歴史 学を取っている学生にとっては明白でしょう。まず有用なアイデアから 始まり、そして新しい層を、それが既存の枠組みのコンセプトに照らし 合わせて検討するということをせずに、繰り返し層を追加していくので す。リファクタリングとは Microsoft には存在しない概念です。せい ぜい良くても、古いインターフェースを非推奨(deprecate)にするくら いでしょうが、普通はそんな脅しは全く無力なものです。Win 2000 に おいて、ドキュメント化されていない非推奨の Win16 API がいったい いくつあると思いますか?

悪い設計は、考え方が混乱している徴候です。そういうのは、基礎 となるライブラリを書く人には役に立たない特質です。


<< WsControl() 内部解析 プロセス間でのソケットの受け渡し >>
Last modified: $Id: csocket.html,v 1.5 2002/11/09 20:40:32 ksk Exp $ Go to the original FAQ page
< Go to the main FAQ page << Go to the Home Page