Windows ソケット : アーカイブ付きソケットの動作
更新 : 2007 年 11 月
ここでは、CSocket、CSocketFile、CArchive の各オブジェクトを組み合わせて、Windows ソケット経由で簡単にデータを送受信する方法について説明します。
「Windows ソケット : アーカイブを使用するソケットの例」には、PacketSerialize 関数の例があります。PacketSerialize の例で使っているアーカイブ オブジェクトの動作は、MFC の Serialize 関数に渡されるアーカイブ オブジェクトによく似ています。ただし、ソケットの場合は、アーカイブの接続先が標準の CFile オブジェクト (通常、ディスク ファイルに対応) ではなく、CSocketFile オブジェクトです。CSocketFile オブジェクトはディスク ファイルに接続されず、CSocket オブジェクトに接続されます。
CArchive オブジェクトは、バッファを管理します。格納 (送信) 用アーカイブのバッファの空き領域がなくなると、対応する CFile オブジェクトがバッファの内容を書き出します。ソケットに接続されているアーカイブのバッファをフラッシュすることは、メッセージの送信に相当します。読み込み (受信) 用アーカイブのバッファの空き領域がなくなると、バッファが再び利用できるようになるまで、CFile オブジェクトは読み込みを停止します。
CSocketFile は CFile からの派生クラスですが、CFile のメンバ関数のうち、位置指定用関数 (Seek、GetLength、SetLength など)、ロック用関数 (LockRange、UnlockRange)、GetPosition 関数はサポートしません。CSocketFile オブジェクトは、対応する CSocket オブジェクトとのバイト シーケンスのやりとりだけを行います。ファイルが含まれていないので、Seek や GetPosition などの操作は意味がありません。CSocketFile は CFile から派生しているので、通常はこのクラスのすべてのメンバ関数を継承します。この継承を行わないようにするには、サポートしない CFile のメンバ関数を CSocketFile でオーバーライドし、CNotSupportedException をスローします。
CSocketFile オブジェクトは、CSocket オブジェクトのメンバ関数を呼び出して、データの送受信を任せます。
次の図は、通信の両端にあるこれらのオブジェクトの関係を示したものです。
CArchive、CSocketFile、CSocket
このように複雑になっているのは、ソケットの詳細部分をプログラマが管理しないで済むようにするためです。ソケット、ファイル、およびアーカイブを作成すると、データをアーカイブに挿入するだけで送信でき、データをアーカイブから取り出すだけで受信できます。背後の細かい処理は、CArchive、CSocketFile、および CSocket が管理します。
CSocket オブジェクトは、非同期状態 (通常の状態) と同期状態という 2 つの状態を持つオブジェクトです。非同期状態のソケットは、フレームワークからの非同期通知を受信できます。ただし、データの送受信中には、ソケットは同期状態になります。この同期動作が完了するまで、非同期通知を受け取ることはできません。ソケットのモードは切り替わるので、次のような操作を実行できます。
void CMySocket::OnReceive(int nErrorCode)
{
if (0 == nErrorCode)
{
CSocketFile file(this);
CArchive ar(&file, CArchive::load);
CString str;
ar >> str;
}
}
CSocket を 2 状態オブジェクトとして実装しないと、前の通知の処理中に、同種のイベントの通知をさらに受け取ることがあります。たとえば、OnReceive の処理中にさらに OnReceive 通知を受け取ることがあります。前に示したコードでは、str をアーカイブから取り出すと再帰動作が発生します。CSocket では、状態を切り替えることによって後続の通知を防ぎ、再帰を防止しています。つまり、通知の中に通知を入れ子にしないようにしています。
メモ : |
---|
CSocketFile は、CArchive オブジェクトと組み合わせずに (制限付きの) ファイルとして扱うこともできます。既定では、CSocketFile コンストラクタのパラメータ bArchiveCompatible は TRUE です。これは、ファイル オブジェクトがアーカイブと組み合わせて使われることを示しています。ファイル オブジェクトをアーカイブなしで使うには、パラメータ bArchiveCompatible で FALSE を渡します。 |
"アーカイブ互換" モードでは、CSocketFile オブジェクトのパフォーマンスは向上し、"デッドロック" の危険性は減少します。デッドロックとは、送信側と受信側の両ソケットが待機状態になっているか、共通リソースが競合する状態のことです。この状況は、CArchive オブジェクトが CFile オブジェクトと組み合わされて動作したときと同じように、CArchive オブジェクトが CSocketFile オブジェクトと組み合わされて動作したときに発生することがあります。CFile との連係時のアーカイブは、受信したバイト数が要求数より少ないと、ファイルの終わりに達したと判断します。しかし、CSocketFile の場合は、データがメッセージ単位であるため、バッファに複数のメッセージを保持できます。したがって、受信したバイト数が要求バイト数より少なくても、ファイルの終わりに達したとは判断しません。アプリケーションは CFile で行ったようなブロックをせずに、バッファが空になるまでメッセージの読み取りを続けます。この時、アーカイブのバッファの状態は、CArchive の IsBufferEmpty 関数で監視できます。
詳細については、「Windows ソケット : アーカイブ付きソケットの使用」を参照してください。