WCF 入門 (2) ~ 初めての WCF プログラムの説明
前回の記事では、極めて簡単な WCF プログラムを作りました。 メッセージの受信プログラムが HelloWCFReceiver.exe であり、送信プログラムが HelloWCFSender.exe です。
それぞれのプログラムがどのようにして通信するのか、簡単にみておきましょう。
受信プログラム HelloWCFReceiver.exe の動作
まずはソースコードの説明です。
using System;
using System.ServiceModel;
sealed class HelloWCFReceiverApp {
クラス名は HelloWCFReceiverApp です。この名前は任意の名前で OK です。プログラムのエントリポイントは、次の Main です。
static void Main() {
次のコードで、どこでどのようにデータを受信するのか指定します。
Uri address = new Uri ( "http://localhost:4000/IHelloWCF" ); BasicHttpBinding binding = new BasicHttpBinding();
Uri として http://localhost:4000/IHelloWCF を指定しています。これにより、接続を受け付けるホスト名、ポート番号及びサービスコントラクト (ServiceContract) を指定しています。
また、バインディングとして BasicHttpBinding を指定することにより、通常の HTTP プロトコルで通信することを指定しています。
ServiceHost svc = new ServiceHost( typeof(HelloWCF) ); svc.AddServiceEndpoint( typeof(IHelloWCF), binding, address ); svc.Open();
サービスエンドポイントのオープンをした後、netstat で TCP ポートの状態を見ると確かに 4000 番でリスニングしています。 また PID 4 はシステムであることを表しています。
> netstat -ano | findstr 4000 TCP 0.0.0.0:4000 0.0.0.0:0 LISTENING 4 TCP [::]:4000 [::]:0 LISTENING 4 >
WFetch を用いて、ポート 4000 番にでたらめな要求をすると次のように応答がありました。 確かに HTTP の応答です。ついでに HTTP API 2.0 が応答を返している、ということもわかります。
Console.WriteLine ( "HelloWCFReceiverApp: Wainting for messages..." ); Console.ReadLine(); svc.Close(); } }
受信側のメインスレッドは ReadLine で停止させておきます。
ちなみに、メインスレッドをこのように止めているので、「何も身動きがとれないのでは?」 と疑問に思うかもしれませんが、 次のようにデバッガをアタッチしてみるとわかるように、スレッドが複数起動されていることがわかります。
0:004> ~*kbn 100 0 Id: 1f50.1bac Suspend: 1 Teb: 7ffde000 Unfrozen # ChildEBP RetAddr Args to Child 00 0028ebd8 773d8d94 773e9522 00000014 0028ec3c ntdll!KiFastSystemCallRet 01 0028ebdc 773e9522 00000014 0028ec3c 0028ec3c ntdll!NtRequestWaitReplyPort+0xc 02 0028ebfc 75ca6b7b 0028ec3c 00010588 0002021d ntdll!CsrClientCallServer+0xc2 03 0028ecf8 75ca6cb5 00000003 01c076f0 00000100 KERNEL32!ReadConsoleInternal+0x1cd 04 0028ed80 75c5a091 00000003 01c076f0 00000100 KERNEL32!ReadConsoleA+0x40 05 0028ede8 0018a907 00000000 01c076f0 00000100 KERNEL32!ReadFile+0x84 WARNING: Frame IP not in any known module. Following frames may be wrong. 06 0028ee20 706f9c83 01c076f0 01c074c4 0028ee80 0x18a907 07 0028ee68 706f9d3e 0028ee80 00000000 00000100 mscorlib_ni+0x789c83 08 0028ee90 70169f5b 00000100 00000000 01c07508 mscorlib_ni+0x789d3e 09 0028eeac 70169dac 003c9d80 00000000 00000000 mscorlib_ni+0x1f9f5b 0a 0028eecc 706fcc95 01b95140 0028eef0 00880110 mscorlib_ni+0x1f9dac 0b 0028eef0 71571b4c 0028ef3c 00000000 0028ef80 mscorlib_ni+0x78cc95 0c 0028ef00 715821b1 0028efd0 00000000 0028efa0 mscorwks!CallDescrWorker+0x33 0d 0028ef80 71596501 0028efd0 00000000 0028efa0 mscorwks!CallDescrWorkerWithHandler+0xa3 0e 0028f0b8 71596534 0036c030 0028f184 0028f150 mscorwks!MethodDesc::CallDescr+0x19c 0f 0028f0d4 71596552 0036c030 0028f184 0028f150 mscorwks!MethodDesc::CallTargetWorker+0x1f 10 0028f0ec 715efa45 0028f150 1761b4ef 00000000 mscorwks!MethodDescCallSite::CallWithValueTypes+0x1a 11 0028f250 715ef965 00363000 00000001 0028f28c mscorwks!ClassLoader::RunMain+0x223 12 0028f4b8 715efeb5 00000000 1761bf37 00000001 mscorwks!Assembly::ExecuteMainMethod+0xa6 13 0028f988 715f009f 000d0000 00000000 1761bf67 mscorwks!SystemDomain::ExecuteMainMethod+0x456 14 0028f9d8 715effcf 000d0000 1761bc9f 00000000 mscorwks!ExecuteEXE+0x59 15 0028fa20 72097c24 00000000 71570000 0028fa3c mscorwks!_CorExeMain+0x15c 16 0028fa30 75c44911 7ffdf000 0028fa7c 773be4b6 mscoree!_CorExeMain+0x2c 17 0028fa3c 773be4b6 7ffdf000 654d008e 00000000 KERNEL32!BaseThreadInitThunk+0xe 18 0028fa7c 773be489 72097bf0 7ffdf000 00000000 ntdll!__RtlUserThreadStart+0x23 19 0028fa94 00000000 72097bf0 7ffdf000 00000000 ntdll!_RtlUserThreadStart+0x1b 1 Id: 1f50.1ff8 Suspend: 1 Teb: 7ffdd000 Unfrozen # ChildEBP RetAddr Args to Child 00 01b8f7f4 773d9244 75c4c3e4 00000003 01b8f848 ntdll!KiFastSystemCallRet 01 01b8f7f8 75c4c3e4 00000003 01b8f848 00000001 ntdll!ZwWaitForMultipleObjects+0xc 02 01b8f894 75c4c64e 01b8f848 01b8f8d8 00000000 KERNEL32!WaitForMultipleObjectsEx+0x11d 03 01b8f8b0 71602541 00000003 01b8f8d8 00000000 KERNEL32!WaitForMultipleObjects+0x18 04 01b8f910 7160249e 16f1bfff 00000000 00000000 mscorwks!DebuggerRCThread::MainLoop+0xe9 05 01b8f940 716023c5 16f1bfcf 00000000 00000000 mscorwks!DebuggerRCThread::ThreadProc+0xe5 06 01b8f970 75c44911 00000000 01b8f9bc 773be4b6 mscorwks!DebuggerRCThread::ThreadProcStatic+0x9c 07 01b8f97c 773be4b6 00000000 64dd034e 00000000 KERNEL32!BaseThreadInitThunk+0xe 08 01b8f9bc 773be489 7160237f 00000000 00000000 ntdll!__RtlUserThreadStart+0x23 09 01b8f9d4 00000000 7160237f 00000000 00000000 ntdll!_RtlUserThreadStart+0x1b 2 Id: 1f50.1778 Suspend: 1 Teb: 7ffdc000 Unfrozen # ChildEBP RetAddr Args to Child 00 03e8f960 773d9244 75c4c3e4 00000002 03e8f9b4 ntdll!KiFastSystemCallRet 01 03e8f964 75c4c3e4 00000002 03e8f9b4 00000001 ntdll!ZwWaitForMultipleObjects+0xc 02 03e8fa00 75c4c64e 03e8f9b4 71ab8d28 00000000 KERNEL32!WaitForMultipleObjectsEx+0x11d 03 03e8fa1c 71692bcb 00000002 71ab8d28 00000000 KERNEL32!WaitForMultipleObjects+0x18 04 03e8fa3c 71697028 003c9218 03e8fb40 003c9248 mscorwks!WKS::WaitForFinalizerEvent+0x77 05 03e8fa50 7159845f 03e8fb40 00000000 00000000 mscorwks!WKS::GCHeap::FinalizerThreadWorker+0x49 06 03e8fa64 715983fb 03e8fb40 03e8faec 7167759b mscorwks!Thread::DoADCallBack+0x32a 07 03e8faf8 71598321 03e8fb40 14a1bd8b 00000000 mscorwks!Thread::ShouldChangeAbortToUnload+0xe3 08 03e8fb34 715ef6cc 03e8fb40 00000000 003c5900 mscorwks!Thread::ShouldChangeAbortToUnload+0x30a 09 03e8fb5c 715ef6dd 71696fdd 00000008 03e8fba4 mscorwks!ManagedThreadBase_NoADTransition+0x32 0a 03e8fb6c 7163c63c 71696fdd 14a1bd1b 00000000 mscorwks!ManagedThreadBase::FinalizerBase+0xd 0b 03e8fba4 71692015 00000000 00000000 00000000 mscorwks!WKS::GCHeap::FinalizerThreadStart+0xbb 0c 03e8fc3c 75c44911 003c9248 03e8fc88 773be4b6 mscorwks!Thread::intermediateThreadProc+0x49 0d 03e8fc48 773be4b6 003c9248 668d067a 00000000 KERNEL32!BaseThreadInitThunk+0xe 0e 03e8fc88 773be489 71691fcf 003c9248 00000000 ntdll!__RtlUserThreadStart+0x23 0f 03e8fca0 00000000 71691fcf 003c9248 00000000 ntdll!_RtlUserThreadStart+0x1b 3 Id: 1f50.1e18 Suspend: 1 Teb: 7ffdb000 Unfrozen # ChildEBP RetAddr Args to Child 00 040bfa64 773d8ce4 75c4523a 000001f0 040bfaec ntdll!KiFastSystemCallRet 01 040bfa68 75c4523a 000001f0 040bfaec 040bfaac ntdll!ZwRemoveIoCompletion+0xc 02 040bfa94 7163e4fc 000001f0 040bfae4 040bfaec KERNEL32!GetQueuedCompletionStatus+0x29 03 040bfb04 71692015 00000000 00000000 00000000 mscorwks!ThreadpoolMgr::CompletionPortThreadStart+0x141 04 040bfc28 75c44911 004496f0 040bfc74 773be4b6 mscorwks!Thread::intermediateThreadProc+0x49 05 040bfc34 773be4b6 004496f0 616e0686 00000000 KERNEL32!BaseThreadInitThunk+0xe 06 040bfc74 773be489 71691fcf 004496f0 00000000 ntdll!__RtlUserThreadStart+0x23 07 040bfc8c 00000000 71691fcf 004496f0 00000000 ntdll!_RtlUserThreadStart+0x1b # 4 Id: 1f50.f24 Suspend: 1 Teb: 7ffda000 Unfrozen # ChildEBP RetAddr Args to Child 00 0420fdb4 7740d0d0 61450716 00000000 00000000 ntdll!DbgBreakPoint 01 0420fde4 75c44911 00000000 0420fe30 773be4b6 ntdll!DbgUiRemoteBreakin+0x3c 02 0420fdf0 773be4b6 00000000 614504c2 00000000 KERNEL32!BaseThreadInitThunk+0xe 03 0420fe30 773be489 7740d094 00000000 00000000 ntdll!__RtlUserThreadStart+0x23 04 0420fe48 00000000 7740d094 00000000 00000000 ntdll!_RtlUserThreadStart+0x1b 0:004>
ちなみに、上記のスレッドはそれぞれ次の役割です。
スレッド | 役割 |
---|---|
#0 | メインスレッド (これは Console.ReadLine で停止している) |
#1 | アイドル状態の CLR デバッガスレッド |
#2 | アイドル状態のファイナライザスレッド |
#3 | アイドル状態の CLR IOCP スレッド (このスレッドがデータを受け取る) |
#4 | デバッガスレッド |
ここでは、スレッド #3 がデータを受け取りそれを処理しているようです。
ちなみに、IOCP (I/O Completion Port, I/O 完了ポート) がよくわからない方は、 当サイトの 非同期 I/O 完了ポート を見てください。
送信プログラム HelloWCFSender.exe の動作
メッセージの送信側プログラムでは、最初にアドレス、バインド情報をセットします。 これは受信側と同じです。
using System; using System.ServiceModel; using System.ServiceModel.Channels; sealed class HelloWCFSender { static void Main() { Uri address = new Uri ( "http://localhost:4000/IHelloWCF" ); BasicHttpBinding binding = new BasicHttpBinding();
次にチャネルファクトリを作ります。 このときにエンドポイントとサービスコントラクトの型 (インターフェイス) を指定します。
ChannelFactory<IHelloWCF> factory = new ChannelFactory<IHelloWCF>( binding, new EndpointAddress(address) ); IHelloWCF proxy = factory.CreateChannel(); proxy.Say( "Hello, WCF!" ); } }
上記で作成したチャネルファクトリで作成したチャネルオブジェクトは、IHelloWCF インターフェイス (サービスコントラクト) を実装します。そこれで、直接 Say メソッドを呼ぶことができます。