Winsock Programmer's FAQ
第7章: 論説記事: プロセス間でのソケットの受け渡し

プロセス間でのソケットの受け渡し

Warren Young、Frank Schmied 著

ソケットをあるプロセスから他のプロセスに受け渡したいと思った とき、その「正式に認可されている」方法は Winsock 2 の新しい関数 WSADuplicateSocket() を使う方法だけです。この FAQ ではもともと、この問題を以下のように扱っていました。

    Winsock 2 では、 WSADuplicateSocket() を通し てこの機能をサポートしています。仕様書のセクション 2.10 には この方法について詳しく記述されており、また MSDN には、この関 数のドキュメントの中で、ステップ毎の手順が示されています。ま た、Microsoft Knowledge Base の Q150523 の記事も読んでおくと良いでしょう。この記事には、各種の Windows 間で、ソケットの継承がどのように異なっているかについ て記述されています。

    他に Win32 API の面白い機能として、新規プロセスを生成する ときに、それに異なる「標準ハンドル」(標準入力、標準出力、標 準エラー)を与えることができる、というものがあります。MSKB の 記事 Q190351 がこの方法について記述しています。この機能は子プロセスに対し てしか使えない、ということに注意してください。つまり自分自身 の標準入出力ハンドルをソケットにリダイレクトすることはできま せん。またこの方法を行うと、プロセスによっては振舞いがおかし くなるものもある、という注意点もあります。この機能は Unix の 世界の dup2() ほど強力なことはできない、という ことははっきりしています。

もともとの FAQ では次に 、この方法は Winsock 1.1 ではできない、 と述べていました。しかし結局は、Frank Schmied 氏によって、多少ト リッキーな方法ではありますが、Win32 上の Microsoft Winsock 1.1 においてこれを行う方法が示されました。Frank 氏は以下のように述べ ています。

    Winsock 1.1 では、Win32 の DuplicateHandle() 呼出しを使って、ソケットをあるプロセスから別のプロセスへ渡す ことができます。この呼出しの扱い方は非常に複雑になることがあ ります。実際、「本当の」プロセスハンドルを生成するのは思った ほど簡単ではありません。Windows は二種類のウィンドウハンドル、 すなわち仮想ハンドルと実ハンドルとを使っています。Windows に おけるハンドルとは通常メモリアドレスであり、インスタンスハン ドルは現在のアドレス空間におけるコード開始オフセットポインタ 以外の何者でもありません。従って、プロセスの HINSTANCE ハン ドル(仮想ハンドルすなわちローカルハンドル)は、通常 0x4000000 になります。ハンドルの「この値」を別のプロセスに持っていって も動作しません。プロセスの「本当の」ハンドルを得るためには、 OpenProcess() を使います。

                OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
    

    ハンドルの複製は以下のようになります。

                SOCKET ConvertProcessSocket(SOCKET oldsocket, DWORD source_pid)
                {
                    HANDLE source_handle = OpenProcess(PROCESS_ALL_ACCESS,
                            FALSE, source_pid);
                    HANDLE newhandle;
                    DuplicateHandle(source_handle, (HANDLE)oldsocket,
                            GetCurrentProcess(), &newhandle, 0, FALSE,
                            DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
                    CloseHandle(source_handle);
                    return (SOCKET)newhandle;
                }
    

    この方法は、私のマルチプロセスWebサーバではちゃんと動いて います。上記の関数は、新しいプロセスにおいてソケットを複製し、 古いプロセスにおいてハンドルを閉じます。このハンドルは、古い ハンドルと同じアクセス属性を持ちますが、子プロセスには引き継 がれません。この振舞いを変更するには、 DuplicateHandle() 呼出しのところの FALSE をTRUE に替えてください。見てわかる通り、提供側プロセスのハンドルは 仮想ハンドルでもよいですが、相手側プロセスのハンドルは本当の ハンドルでなくてはなりません

私の理解ではこれは、元プロセスがローカル SOCKET ハンドルを OpenProcess() を使って実ハンドルに変換し、そしてそ の値とプロセスIDを相手先プロセスに渡す、というものです。そして相 手先プロセスはそれを Winsock で使えるように、 Frank 氏の ConvertProcessSocket() 関数を使って実ハンドルからロー カルハンドルへと変換するわけです。ここで、 DuplicateHandle() 呼び出しによって元プロセスのハン ドルを close して、そしてCloseHandle()呼び出しによっ て相手先プロセスに渡された実ハンドルを close している、という点 に注意して下さい。

警告: Frank 氏のテクニックは、おそらく Microsoft の実 装でしか動作しません。Win16 では動作しませんし、WinCE でも動作し ないかもしれません。Layered Service Providers が存在していると動 くかもしれないし動かないかもしれません。例外は Windows NT 4.0 SP 4 以降で、これにはインストール可能ファイルシステム(IFS)層にパッ チが当たっていてこの種のものが動くようになっています。またこのテ クニックは、第2火曜日で上弦の月の日は、プログラムの実行前に "oompa hoozah" の呪文をゆっくり四回唱えないと、動作しないかもし れません。簡単に言うと、このテクニックは詐欺なのです。私はちゃん と警告しましたからね。:)

Copyright © 2000 by Warren Young and Frank Schmied. All rights reserved.


<< CSocket はなぜ有害か? ファイヤウォールとの付き合い方 >>
Last modified: $Id: passing-sockets.html,v 1.6 2002/11/09 20:40:33 ksk Exp $ Go to the original FAQ page
< Go to the main FAQ page << Go to the Home Page