学习 WCF By Visual Studio 2010 (1)--起步“全双工”
零起点学吧,以前想学.NET Remoting,我懒人一个到现在也没开始学,这倒好直接WCF吧。
(一)起步“全双工”(Duplex)
1 建立一个 服务契约在Visual Studio 中建立一个 “WCF Service Library” 项目
1.1 建立一个 服务接口 就叫 "ISendMessageService" 吧
2 {
3 [ServiceContract(
4 Name = "SendMessageService",
5 Namespace = "SendMessageServiceNameSpace",
6 SessionMode = SessionMode.Required,
7 CallbackContract = typeof(IMessageCallback))]
8 public interface ISendMessageService
9 {
10 [OperationContract]
11 string SendMessage(string value);
12
13 }
14 }
1.2 实现这个接口
2 {
3 private static List<IMessageCallback> _callbackList = new List<IMessageCallback>();
4
5 #region ISendMessageService Members
6
7 public string SendMessage(string message)
8 {
9 IMessageCallback callBack = OperationContext.Current.GetCallbackChannel<IMessageCallback>();
10
11 if (!_callbackList.Contains(callBack))
12 {
13 _callbackList.Add(callBack);
14 }
15
16 _callbackList.ForEach(
17 delegate(IMessageCallback callback)
18 { callback.NotifyUI(message); });
19 return string.Format("Send message at{0}:{1}", DateTime.Now, message);
20 }
21 #endregion
22 }
1.3 建立Callback 接口
2 //所以IMessageCallback不再需要添加ServiceContractAttribute特性。
3 public interface IMessageCallback
4 {
5 //由于服务端不需要回调的返回值,索性将回调操作也设为单向方法。
6 [OperationContract(IsOneWay = true)]
7 void NotifyUI(string message);
8 }
1.4 服务端配置文件:
2 <configuration>
3 <system.serviceModel>
4 <services>
5 <service name="MessageService.MessageService">
6 <endpoint address="Service" binding="wsDualHttpBinding" contract="MessageService.ISendMessageService" name="HttpBinding">
7 <identity>
8 <dns value="localhost"/>
9 </identity>
10 </endpoint>
11 <endpoint address="Service" binding="netTcpBinding" contract="MessageService.ISendMessageService" name="TcpBinding">
12 <identity>
13 <dns value="localhost"/>
14 </identity>
15 </endpoint>
16 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
17 <host>
18 <baseAddresses>
19 <add baseAddress="http://localhost:8732/SendMessage/MessageService/"/>
20 <add baseAddress="net.tcp://localhost:8733/SendMessage/MessageService/"/>
21 </baseAddresses>
22 </host>
23 </service>
24 </services>
25 <behaviors>
26 <serviceBehaviors>
27 <behavior>
28 <serviceMetadata httpGetEnabled="True"/>
29 <serviceDebug includeExceptionDetailInFaults="False"/>
30 </behavior>
31 </serviceBehaviors>
32 </behaviors>
33 </system.serviceModel>
34 </configuration>
2 建立客户端
2.1 添加 Service References
2.2 如果是 通过 Http 实现 Duplex 则还需要修改 App.config 否则在Xp sp2 下回报 80端口已经被占用的异常。
clientBaseAddress="http://localhost:9999/MyClient
2 <binding name="HttpBinding" closeTimeout="00:01:00" openTimeout="00:01:00"
3 receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"
4 transactionFlow="false" hostNameComparisonMode="StrongWildcard"
5 maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
6 messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" clientBaseAddress="http://localhost:9999/MyClient">
7 <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
8 maxBytesPerRead="4096" maxNameTableCharCount="16384" />
9 <reliableSession ordered="true" inactivityTimeout="00:10:00" />
10 <security mode="Message">
11 <message clientCredentialType="Windows" negotiateServiceCredential="true"
12 algorithmSuite="Default" />
13 </security>
14 </binding>
15 </wsDualHttpBinding>
2.3 客户端建立 Service Reference 实体:
2 {
3 _sendMessageServiceClient = new SendMessageServiceClient(new InstanceContext(this), "TcpBinding");
4 _sendMessageServiceClient.Open();
5 }
2.4 调用服务端方法。
2 {
3 txt_receive.Text = _sendMessageServiceClient.SendMessage(txt_Message.Text);
4 }
2.5 实现回调接口 SendMessageServiceCallback
2 {
3 private SendMessageServiceClient _sendMessageServiceClient;
4 private SynchronizationContext _uiSyncContext = null;
5
6 public Client()
7 {
8 InitializeComponent();
9 }
2 #region SendMessageServiceCallback Members
3
4 void SendMessageServiceCallback.NotifyUI(string guestName)
5 {
6 txt_Callbacktest.Text = guestName;
7 }
8
9 #endregion
大功告成! 这一下就可以在回调的时候更改txt_Callbacktest.Text 属性了。
可是真的这么顺利么?.....
问题:TimeOut!!
为什么会 TimeOut? 转自tomore的日志
我们现在来分析是什么导致了TimeoutException的抛出。原因很简单:由于我们对service的调用的是在UI 线程调用的,所以在开始调用到最终得到结果,这个UI Thread会被锁住;但是当service进行了相应的运算的到运算的结果后,需要调用callback对象对client进行回调,默认的情况下,Callback的执行是在UI线程执行的。当Callback试图执行的时候,发现UI 线程被锁,只能等待。这样形成一个死锁,UI线程需要等待CalculateService执行返回后才能解锁,而CalculateService需要Callback执行完成;而Callback需要等到UI线程解锁才能执行。
更多的请参见:
http://www.winu.cn/space-14160-do-blog-id-25131.html
——————————————————————————————————————————————————————————————
学习资源:
WCF从理论到实践
http://kb.cnblogs.com/page/43709/
《我的WCF之旅》博文系列汇总
http://www.cnblogs.com/artech/archive/2007/09/15/893838.html