服务器端回调有啥用呢?
比如:
向服务器上传了一个文件,但是,为了节约空间或出于其他目的,服务器要对刚上传的文件进行处理(压缩或者多媒体文件转码),这些操作无法马上向客户端回复,而客户端也不可能就停在这里一直在等。我们希望,在客户端上传文件后马上返回,而服务器对文件处理完成后再通知一下客户端。
这样就引出一个东东——回调,E文叫Call Back。我估计用E文表述可能更好理解,Call back就是相对于Call to而言的,即调用的方向与Call to相反。
在WCF中使用回调,只需要多定义一个接口即可,这个接口的方法和服务协定一样,要附加OperationContractAttribute特性。
然后在定义服务协定时,在ServiceContractAttribute的CallbackContract中设置一个回调接口的Type。
在服务操作中,通过OperationContext的GetCallbackChannel方法取出回调协定的实例,调用回调的方法,就会在客户端寻找回调接口的实现类并调用对应的成员。
下面我们做一个摇号程序进行说明
一、服务端
/// <summary> /// 1、定义一个回调接口 /// </summary> public interface ICallback { [OperationContract(IsOneWay = true)] void CallClient(int value); } /// <summary> /// 2、定义服务协定 /// </summary> [ServiceContract(Namespace="MyNamespace", CallbackContract=typeof(ICallback),//标注回调协定 SessionMode=SessionMode.Required //要求会话 )] public interface IService { /// <summary> /// 会话从调用该操作开始 /// </summary> [OperationContract(IsOneWay=true,IsInitiating=true,IsTerminating=false)] void CallServerOperation(); /// <summary> /// 调用该操作后,会话结束 /// </summary> [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)] void End(); } /// <summary> /// 3、实现服务协定 /// </summary> public class MyService : IService,IDisposable { private ICallback m_cb = null; //回调接口 private System.Threading.Timer m_timer = null; //计时器,定时干活 private Random m_random = null;//随机数 public void CallServerOperation() { m_cb = OperationContext.Current.GetCallbackChannel<ICallback>(); m_random = new Random(); //生成随机数,并回调到客户端;每3秒执行一次 m_timer = new System.Threading.Timer((obj) => m_cb.CallClient(m_random.Next()), null, 10, 3000); } public void End() { Console.WriteLine("会话结束"); } public void Dispose() { m_timer.Dispose(); Console.WriteLine("{0}-服务实例已释放.", DateTime.Now.ToLongTimeString()); } }
//配置服务器 static void Main() { Console.Title = "WCF服务端"; //服务基地址 Uri baseUri = new Uri("http://localhost:3000/Service"); //声明服务器主机 using (ServiceHost host = new ServiceHost(typeof(MyService), baseUri)) { /*既支持会话传输速度又快的非TCP莫属了,所以这里我选择NetTcpBinding; * 这样在默认行为下,每启动一个会话就创建一个服务实例,而当会话结束时就会释放。 */ //添加绑定和终结点 NetTcpBinding binding = new NetTcpBinding(); binding.Security.Mode = SecurityMode.None; host.AddServiceEndpoint(typeof(IService), binding, "net.tcp://localhost:1211/rr"); //添加服务描述 host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true }); //启动服务 try { host.Open(); Console.WriteLine("服务已启动"); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadKey(); //关闭服务 host.Close(); } }
二、客户端
在客户端实现回调接口
/// <summary> /// 实现回调接口 /// </summary> public class MyCallback : WS.IServiceCallback { /// <summary> /// 回调引发该事件 /// </summary> public event EventHandler<int> ValueCallback; /// <summary> /// 因为该方法是由服务器调用的 /// 如果希望在客户端能及时作出响应,应当使用事件 /// </summary> /// <param name="value">value</param> public void CallClient(int value) { if (ValueCallback != null) { ValueCallback(this, value); } } }
public partial class Form1 : Form { WS.ServiceClient m_sc; readonly MyCallback m_mcallback; public Form1() { InitializeComponent(); m_mcallback = new MyCallback(); m_mcallback.ValueCallback += mc_ValueCallback; labNum.Text = ""; btnStop.Enabled = false; } void mc_ValueCallback(object sender, int e) { labNum.Text = e.ToString(); } private void btnStart_Click(object sender, EventArgs e) { m_sc = new WS.ServiceClient(new System.ServiceModel.InstanceContext(m_mcallback)); m_sc.CallServerOperation(); btnStart.Enabled = false; btnStop.Enabled = true; } private void btnStop_Click(object sender, EventArgs e) { m_sc.End(); btnStart.Enabled = true; btnStop.Enabled = false; } }
效果如下: