WCF服务问题之-回调

 

 

在使用服务时虽然一般是客户端向服务端请求服务,但有些时候也需要服务端向客户端进行通知(Notify),在CS的程序中尤为常见,在出现WCF之前,Remoting中使用回调是大费周章的一件事情,需要建立单独的侦听类,并且要处于独立的程序集中才行,在WCF中大大简化了回调过程,但是也有一些需要特别注意的地方,如果不注意回调也不是那么容易的,先看一个简单的回调代码:

一:配置属性声明:

服务接口:

 [ServiceContract(CallbackContract = typeof(IChartCallBack))]    public interface IChart
    {

        [OperationContract]
        void Join(string name);

 

        [OperationContract]
        void Leave(string name);

        [OperationContract]
        void Speek(string namestring desnamestring message          );
    }

服务端实现:

  [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant,
     InstanceContextMode = InstanceContextMode.PerCall)]
public abstract class ChartIChart
{
        public void Join(string name)
        {
             //……code
             IChartCallBack callback = OperationContext.Current.GetCallbackChannel<IChartCallBack>();
if (callback != null)
 {
                callback.NotifyJoin(name);  //通知客户端
           }
    }
    //剩余代码
}

回调接口:

[ServiceContract]
   public interface IChartCallBack
    {
     [OperationContract(IsOneWay = true)]
        void NotifyJoin(string name);
     [OperationContract(IsOneWay = true)]
        void NotifyLeave(string name);
    [OperationContract(IsOneWay = true)]
        void SpeekTo(string namestring message);

     }

客户端实现:

[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant,UseSynchronizationContext=false)]
    public  class ClientIChartCallBack //实现了聊天回调窗口
    {
        public void NotifyJoin(string name)
        {
   Console.WriteLine(name + ” 已加入”);
}

     }

  上面的代码都很简单,用起来也极为简便,但是要注意的地方还是很多,如果不注意这些问题就会出差错,如上面的代码所示回调基本上是在声明式代码中完成的,属性又有不同的选项,不同的选项用处也不一样,要使的服务端能够回调客户端代码,有以下几种方式:

1.    配置服务多线程访问

即设置ServiceBehavior属性的ConcurrencyMode值为Mutiple,这样服务类就允许多线程访问了,但是多线程访问会代码同步问题,所以需要处理好同步问题;

2.    回调重入

配置ServiceBehavior属性的ConcurrencyMode值为Reentrant,服务成为单线程访问,在回调发生时可冲入;

3.    单向回调

在回调接口中的操作契约中设置IsOneWaytrue,设置为单向调用,也可进行回调

如果在使用回调时不遵循这3种方式,回调时会出现异常,无法完成回调工作。


二:客户端处理回调的问题:

由于回调是服务端异步调用,因此会出现调用超时,跨线程访问的问题,如果不认清这些问题的本质,就开始编写代码,往往回调会不成功的,如果使用控制台工程,不使用UI,则跨线程访问UI的问题不会出现,但是超时依然会出现。

1.    超时问题
如将

Console.WriteLine(name + ” 已加入”);

这行代码改成

MessageBox.Show(name);

如果长时间不响应MessageBox,则会抛出异常,所以在回调处理中如果不标记IsOneWaytrue,则尽量不要在处理中采用耗时代码,以免超时。 

2.    WinFrom中回调代码访问UI的问题

   由于回调线程和UI的线程并非同一线程,因此在访问UI时就会出现问题,要解决这一问题,和多线程访问UI是一致的,将访问代码封送到UI线程中来处理,如使用Control.Invoke()函数,或者使用SynchronizationContext同步上下文来完成UI访问任务。

3.    WPF中访问UIElement的问题

   WPF已经不采用Windows消息循环系统了,而采用自行设计的消息系统,因此WPF中处理这种问题和WinForm并不相同,UIElement不在提供Invoke函数来封送代码了,而且也没有SynchronizationContext上下文,但是WPF提供了Dispatcher对象,通过Dispatcher.CurrentDispatcher来获取当前线程(UI线程)的消息分发器对象,Dispatcher有一个Invoke函数,该函数可以实现将另一线程中的代码封送到当前线程中来执行,这样就可以解决跨线程访问UI的问题。

三:安全问题

   上面的代码在单机中运行是没有任何问题的,但是将客户端服务端分到两台PC机中,问题就会出来了,无论调用服务也好回调也好,都会有安全问题,因此需要对安全问题进行配置,安全问题是一个大话题,这里就不细说,为了代码能在多台机器中运行,最简单的就是不使用安全验证,配置文件如下所示:

在配置文件中增加bindings节:

 <bindings>
      <netTcpBinding>
        <binding name="TcpSecurity">
          <security mode="None"> <!--
不采用安全措施-->
            <transport clientCredentialType="None" protectionLevel="None"/>
          </security>
        </binding>
      </netTcpBinding>
  </bindings>

这里使用的是netTcpBinding 如果使用别的绑定,则增加特定绑定的配置代码,在终结点中增加bindingConfiguration属性,并将值指向binding的名称,如

   <endpoint address="net.tcp://localhost:8000/chart"
                  binding="netTcpBinding"
                  contract="fzgk.IChart"
                  bindingConfiguration="TcpSecurity"
                  name="TcpBinding"/>
  客户端也一样增加这些配置项,再次运行程序就不会出现安全问题了。
Posted on 2010-12-23 19:35  风中过客  阅读(1367)  评论(0编辑  收藏  举报