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 メソッドを呼ぶことができます。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説