温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

WCF简单教程(6) 单向与双向通讯

发布时间:2020-06-14 11:01:19 来源:网络 阅读:5706 作者:BoyTNT 栏目:编程语言

第六篇:单向与双向通讯

项目开发中我们时常会遇到需要异步调用的问题,有时忽略服务端的返回值,有时希望服务端在需要的时候回调,今天就来看看在WCF中如何实现。

先看不需要服务端返回值的单向调用,老规矩,直接上代码,再解释。

1、服务端

契约接口中增加一个Sleep方法:

  1. using System;  
  2. using System.ServiceModel;  
  3. using System.Text;  
  4.   
  5. namespace Server  
  6. {  
  7.     [ServiceContract(Namespace="WCF.Demo")]  
  8.     public interface IData  
  9.     {  
  10.         [OperationContract]  
  11.         string SayHello(string userName);  
  12.  
  13.         /// <summary> 
  14.         /// IsOneWay = true 表明这是一个单向调用,注意返回值是void,因为既然是单向调用,客户端肯定不会等待接收返回值的 
  15.         /// </summary> 
  16.         [OperationContract(IsOneWay = true)] 
  17.         void Sleep(); 
  18.     }  


对应的实现类中,我们来实现这个方法:

  1. using System;  
  2. using System.Text;  
  3.   
  4. namespace Server  
  5. {  
  6.     public class DataProvider : IData  
  7.     {  
  8.         public string SayHello(string userName)  
  9.         {  
  10.             return string.Format("Hello {0}.", userName);  
  11.         }  
  12.  
  13.         /// <summary> 
  14.         /// 实现Sleep方法,暂时不做任何事情,只是睡眠5秒 
  15.         /// </summary> 
  16.         public void Sleep() 
  17.         { 
  18.             Thread.Sleep(5000); 
  19.         } 
  20.     }  
  21. }  

App.config就不再列出来了,里面用的是一个netTcpBinding的endpoint。


2、客户端

首先别忘了客户端的契约要与服务端保持一致,App.config也不列出来了,里面有对应的endpoint。主要是调用的代码:

  1. using System;  
  2. using System.ServiceModel;  
  3. using System.ServiceModel.Channels;  
  4.   
  5. namespace Client  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             var proxy = new ChannelFactory<Server.IData>("DataService").CreateChannel();  
  12.  
  13.             //先调用SayHello方法  
  14.             Console.WriteLine(proxy.SayHello("WCF"));  
  15.             //调用一下Sleep方法,按我们的设想,它应该是异步的,所以不会阻塞后面的调用 
  16.             proxy.Sleep(); 
  17.             //再调用一次SayHello方法  
  18.             Console.WriteLine(proxy.SayHello("WCF"));  
  19.             //关闭连接  
  20.             ((IChannel)proxy).Close();  
  21.     }  

按我们的设想,两次SayHello调用之间应该没有延迟,因为Sleep是异步的嘛,编译运行一下,结果……  中间卡住了5秒,这是为什么呢?

这其中涉及到一个并发模型的问题,默认情况下,WCF以单线程模型对外提供服务,也就是说,只能一个一个处理请求,即使是一个OneWay的单向调用,也只能等它处理完后才会接着处理后面的SayHello请求,所以会卡5秒。

并发模式有以下三种,MSDN上的介绍有点复杂,我给简化一下:

Single:单线程调用,请求只能一个一个处理;

Reentrant:可重入的单线程调用,本质仍是单线程,处理回调时,回调请求会进入队列尾部排队;

Multiple:多线程调用,请求是并发的响应的;


调置服务并发模型是在契约的实现类上,我们为DataService类加一个Attribute:

  1. /// <summary> 
  2. /// 用ServiceBehavior为契约实现类标定行为属性,此处指定并发模型为ConcurrencyMode.Multiple,即并发访问 
  3. /// </summary> 
  4. [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)] 
  5. public class DataProvider : IData 
  6.     //略 


这回再编译运行一下,连续打出了2行 Hello WCF,中间不再阻塞了。

 

现在我们再来看看双向通讯的问题。双向通讯可以基于HTTP、TCP、Named Pipe、MSMQ,但要注意,basicHttpBinding和wsHttpBinding不行,要换用wsDualHttpBinding,它会创建两个连接来进行双向通讯。至于TCP,它天然就是双向通讯的。

1、服务端

服务契约要进行修改,增加关于回调的契约:

  1. using System; 
  2. using System.ServiceModel; 
  3. using System.ServiceModel.Description; 
  4.  
  5. namespace Server 
  6.     /// <summary> 
  7.     /// 增加了CallbackContract的标识,用于指明针对此服务契约的回调契约是IDataCallback 
  8.     /// </summary> 
  9.     [ServiceContract(Namespace = "WCF.Demo", CallbackContract = typeof(IDataCallback))] 
  10.     public interface IData 
  11.     { 
  12.         [OperationContract] 
  13.         string SayHello(string userName); 
  14.  
  15.         [OperationContract(IsOneWay = true)] 
  16.         void Sleep(); 
  17.     } 
  18.  
  19.     /// <summary> 
  20.     /// 定义服务回调契约,注意它没有契约标识,只是个一般接口 
  21.     /// </summary> 
  22.     public interface IDataCallback 
  23.     { 
  24.         /// <summary> 
  25.         /// 定义一个回调方法,由于回调不可能要求对方再响应,所以也标识成OneWay的调用,同样不需要有返回值 
  26.         /// </summary> 
  27.        [OperationContract(IsOneWay = true)] 
  28.        void SleepCallback(string text); 
  29.     } 


对应的契约实现类要修改一下:

  1. using System; 
  2. using System.ServiceModel; 
  3. using System.ServiceModel.Description; 
  4. using System.Threading; 
  5. using System.Net; 
  6.  
  7. namespace Server 
  8.     [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)] 
  9.     public class DataProvider : IData 
  10.     { 
  11.         public string SayHello(string userName) 
  12.         { 
  13.             string.Format("Hello {0}.", userName); 
  14.         } 
  15.  
  16.         public void Sleep() 
  17.         { 
  18. //先睡5秒
  19.             Thread.Sleep(5000); 
  20.  
  21. //用OperationContext.Current来获取指定类型的回调对象 
  22.             var callback = OperationContext.Current.GetCallbackChannel<IDataCallback>(); 
  23. //回调SleepCallback方法,并传递参数 
  24.             callback.SleepCallback("睡醒了"); 
  25.         } 
  26.     } 


2、客户端

仍然提醒一下别忘了把新的服务契约更新到客户端。客户端的调用要调整一下:

  1. using System;  
  2. using System.ServiceModel;  
  3. using System.ServiceModel.Channels;  
  4.   
  5. namespace Client  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             //定义一个实现回调接口的类实例 
  12.             var context = new DataCallbackImp(); 
  13.             //创建代理的时候变了,要用DuplexChannelFactory,因为IData契约已经标识了有回调,所以必须要用支持双向通讯的ChannelFactory,传入刚才创建的回调实例 
  14.             var proxy = new DuplexChannelFactory<Server.IData>(context, "DataService").CreateChannel();  
  15.  
  16.             //调用Sleep 
  17.             proxy.Sleep(); 
  18.             //调用SayHello方法  
  19.             Console.WriteLine(proxy.SayHello("WCF"));  
  20.  
  21.             //等待按任意键,先不要关连接 
  22.             Console.ReadKey(); 
  23.             ((IChannel)proxy).Close();  
  24.     }  
  25.  
  26.     /// <summary> 
  27.     /// 实现回调接口中的类,图省事写到这里了 
  28.     /// </summary> 
  29.     class DataCallbackImp : Server.IDataCallback 
  30.     { 
  31.         /// <summary> 
  32.         /// 实现SleepCallback方法 
  33.         /// </summary> 
  34.         public void SleepCallback(string text) 
  35.         { 
  36.             Console.WriteLine("收到回调了:" + text); 
  37.         } 
  38.     } 


编译运行,屏幕先显示一行“Hello WCF.”,过5秒后显示“收到回调了:睡醒了”。

 

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI