WCF 回调的使用

来源:互联网 发布:网络教育的考试 编辑:程序博客网 时间:2024/06/08 08:46

你说,服务器端回调有啥用呢?这样问,估计不好回答,是吧。不急,先讨论一个情景。

假设现有服务器端S,客户端A开始连接S并调用相关操作,其中有一个操作,在功能上有些特殊,调用后无法即时回复,因为在服务器上要作一些后续,而这些处理也许会消耗一定时间,比如:

向服务器上传了一个文件,但是,为了节约空间或出于其他目的,服务器要对刚上传的文件进行处理(压缩或者多媒体文件转码),这些操作无法马上向客户端回复,而客户端也不可能就停在这里一直在等。我们希望,在客户端上传文件后马上返回,而服务器对文件处理完成后再通知一下客户端。

这样就引出一个东东——回调,E文叫Call Back。我估计用E文表述可能更好理解,Call back就是相对于Callto而言的,即调用的方向与Call to相反。

是啊,有必要解释一下,什么叫回调。我讲一个故事吧。

有一天,脑残去书店买书,之前他听别人说有一本书叫《吹牛沉思录》很好看,于是脑残也想买一本。可是,当他到书店后,东找西寻了一番,硬是没看见那本书的影子。

于是,他跑到柜台问工作人员:“我想找《吹牛沉思录》,没找到。”

工作人员马上启动书店的信息管理系统,但可以由于该系统品德不太好,居然用了35秒才启动,然后,工作人员在上面查了一下,回过头说:“抱歉,这本书太抢手了,卖完了,需要拿货。”

脑残追问:“那要啥时候有货?”

工作人员说:“大概两三天后吧,这样吧,你留个联系方式,等到货到了我再联系你。”

……

对的,这就是回调的故事。

脑残(调用方)不知道书店什么时候有货(不清楚调用的操作什么时候返回),但他总不能每天都跑去书店看看,这样太不滑算(消耗资源),于是,书店(被调用方)建议,留下联系方式(只保留内存中函数指针的地址,即回调地址),只要货到了就通知脑残(反调用)。

回调比较典型的一种就是事件,事件驱动模型以前是在VB中被大量使用,后来.NET也继承了这些优点,在此之前,C++/MFC大家都知道的,是通过消息来处理的(消息循环),其实,事件就是对消息的进一步封装,这使得应用更加简便和灵活。

在.NET中我们知道,事件其实就是一个委托,由于委托可以同时绑定多个方法的特点,故被选为事件的表现类型,估计是这样的。

比如,我们常用的,为按钮的Click事件定义一个处理。

button.Click += new EventHandler(onClick)

这样,事件Click的订阅者就是onClick方法,所谓订阅事件,就像我们平时订阅XX杂志一样,只要有新一期发布就发快递给你,你不用天天打电话去杂志社问。

onClick并不是每一刻都去问button:“你被Click了吗?”,onClick就像一个报警系统,只要特定的事件发生,它就会报警。这就是一种回调,onClick不必主动去调用button,只要处于监听状态即可,只要button被Click,onClick就会执行,不用你去调用它。

 

讲了这么多,不知道各位理解了没?

 

在WCF中使用回调,只需要多定义一个接口即可,这个接口的方法和服务协定一样,要附加OperationContractAttribute特性。

然后在定义服务类时,在ServiceContractAttribute的CallbackContract中设置一个回调接口的Type。

在服务操作中,通过OperationContext的GetCallbackChannel方法取出回调协定的实例,调用回调的方法,就会在客户端寻找回调接口的实现类并调用对应的成员。

这样说显然不好理解,还是实践出真知。我们来做一个选号程序:

第一步,定义一个回调接口

  1. public interface ICallback  
  2.  
  3.     // 回调操作也必须One Way  
  4.     [OperationContract(IsOneWay true)]  
  5.     void CallClient(int v);  
  6. }

第二步,定义服务协定。

  1. [ServiceContract(Namespace "MyNamespace"  
  2.     CallbackContract typeof(ICallback),   
  3.     SessionMode SessionMode.Required   
  4.     )]  
  5. public interface IService  
  6.  
  7.     // 会话从调用该操作启动  
  8.     [OperationContract(IsOneWay true  
  9.         IsInitiating true  
  10.         IsTerminating false)]  
  11.     void CallServerOp();  
  12.   
  13.     // 调用该操作后,会话结束  
  14.     [OperationContract(IsOneWay true  
  15.         IsTerminating true  
  16.         IsInitiating false)]  
  17.     void End();  

CallbackContract属性指向ICallback的Type。因为我要使用计时器每隔3秒钟生成一个随机数,并回调到客户端,故要启用会话。

第三步,实现服务协定。

  1. public class MyService IService,IDisposable  
  2.  
  3.     private ICallback m_cb;  
  4.     private Timer timer null;//计时器,定时干活  
  5.     Random rand null;//生成随机整数  
  6.   
  7.     public void CallServerOp()  
  8.      
  9.         this.m_cb OperationContext.Current.GetCallbackChannel();  
  10.         rand new Random();  
  11.         // 生成随整数,并回调到客户端  
  12.         // 每隔3秒生成一次  
  13.         timer new Timer((obj) => m_cb.CallClient(rand.Next()), null10, 3000);  
  14.   
  15.      
  16.   
  17.     public void Dispose()  
  18.      
  19.         timer.Dispose();  
  20.         Console.WriteLine("{0} 服务实例已释放。"DateTime.Now.ToLongTimeString());  
  21.      
  22.   
  23.   
  24.     public void End()  //结束  
  25.      
  26.         Console.WriteLine("会话即将结束。");  
  27.      
  28.  

第四步,完成服务器端的配置。

  1. static void Main(string[] args)  
  2.  
  3.     Console.Title "WCF服务端" 
  4.     // 服务器基址  
  5.     Uri baseAddress new Uri("http://localhost:1378/services");  
  6.     // 声明服务器主机  
  7.     using (ServiceHost host new ServiceHost(typeof(MyService), baseAddress))  
  8.      
  9.         // 添加绑定和终结点  
  10.         // tcp绑定支持会话  
  11.         NetTcpBinding binding new NetTcpBinding();  
  12.         binding.Security.Mode SecurityMode.None;  
  13.         host.AddServiceEndpoint(typeof(IService), binding, "net.tcp://localhost:1211/rr");  
  14.         // 添加服务描述  
  15.         host.Description.Behaviors.Add(new ServiceMetadataBehavior HttpGetEnabled true });  
  16.         try  
  17.          
  18.             // 打开服务  
  19.             host.Open();  
  20.             Console.WriteLine("服务已启动。");  
  21.          
  22.         catch (Exception ex)  
  23.          
  24.             Console.WriteLine(ex.Message);  
  25.          
  26.         Console.ReadKey();  
  27.      
  28. }

既支持会话,传输速度又快的,非TCP莫属了,所以这里我选择NetTcpBinding,这样在默认行为下,每启动一个会话就创建一个服务实例,而当会话结束时就会释放。

======================================================================================

服务器端完工后,下面就是客户端。

第一步,新建一个Windows窗体应用项目(Windows Forms)。

第二步,到对应目录以管理员身份运行服务器端,然后在客户端添加服务引用。

第三步,在客户端实现回调接口

  1. public class MyCallback WS.IServiceCallback  
  2.  
  3.     // 因为该方法是由服务器调用的  
  4.     // 如果希望在客户端能即时作出响应  
  5.     // 应当使用事件  
  6.     public void CallClient(int v)  
  7.      
  8.         if (this.ValueCallbacked != null 
  9.          
  10.             this.ValueCallbacked(thisv);  
  11.          
  12.      
  13.     ///   
  14.     /// 回调引发该事件  
  15.     ///   
  16.     public event EventHandler<</SPAN>intValueCallbacked;  

注意,回调的接口是在客户端实现的,不是服务器端。

第四步,设计窗口。

 

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Data;  
  5. using System.Drawing;  
  6. using System.Linq;  
  7. using System.Text;  
  8. using System.Threading.Tasks;  
  9. using System.Windows.Forms;  
  10.   
  11. namespace Client  
  12.  
  13.     public partial class Form1 Form  
  14.      
  15.         WS.ServiceClient cl null 
  16.         MyCallback cb null 
  17.         public Form1()  
  18.          
  19.             InitializeComponent();  
  20.             cb new MyCallback();  
  21.             cb.ValueCallbacked += cb_ValueCallbacked;  
  22.               
  23.          
  24.   
  25.         void cb_ValueCallbacked(object sender, int e)  
  26.          
  27.             this.lbResult.Text e.ToString();  
  28.          
  29.   
  30.         private void button1_Click(object sender, EventArgs e)  
  31.          
  32.             cl new WS.ServiceClient(new System.ServiceModel.InstanceContext(cb));  
  33.             cl.CallServerOp();  
  34.             button1.Enabled false 
  35.             button2.Enabled true 
  36.          
  37.   
  38.         private void button2_Click(object sender, EventArgs e)  
  39.          
  40.             cl.End();  
  41.             button1.Enabled true 
  42.             button2.Enabled false 
  43.          
  44.      

======================================================================================现在来测试一下吧。


 

转自:http://blog.csdn.net/tcjiaan/article/details/8298486

0 0