WCF双工通讯以及客户端间的间接通讯

原文:WCF双工通讯以及客户端间的间接通讯

  由于学习计划安排不当,对WCF的认知一直停滞不前,最近工作上又用回了WCF,重拾一下,看到蒋老师介绍双工通讯的博文,实践一下,积累一下。原想着WCF的双工通讯就是原本的客户端能调用服务端的方法之余,服务端同样也能调用客户端的方法。把博文看了一遍之后发现这个双工实际上是借助了方法回调实现的。那么下面先介绍一下最基本的双工通讯形式,再介绍一下鄙人利用双工通讯设计了一种形式。

  WCF通讯都是基于方法调用进行信息交互和传递,在开发基本模式的时候也需要往服务端下载元数据信息,从而让客户端知道服务端定义的方法签名,这就是契约;那么转到双工模式下,服务端调用客户端的方法,主调方也要知道方法的签名,这也是通过契约来实现,但是契约的定义并非在定义方法的客户端,仍然是在服务端,服务端定义了契约再由客户端下载了元数据后将其实现。

  下面则定义了一个契约,其目的让客户端往服务端发起连接,等待服务端的回调

1     [ServiceContract(CallbackContract = typeof(ICallback))]
2     interface ILogic
3     {
4         [OperationContract]
5         void ListenToCall();
6     }

 在ServiceContract特性中,使用了CallbackContract,表名了这个契约的回调契约就是ICallback。该回调契约也是自定义的一个接口

1     [ServiceContract]
2     interface ICallback
3     {
4         [OperationContract]
5         List<int> GetHourMinuteFromClient();
6     }

它与普通模式定义的契约一样,就是一个单单纯纯的服务契约而已。但是在服务端想调用客户端的方法时,就是调用这个ICallback接口里面的方法,在这里就是GetHourMinuteFromClient()

在服务端需要实现ILogic接口来实现契约

 1     [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)]
 2     public class LogicService:ILogic
 3     {
 4
 5         public void ListenToCall()
 6         {
 7             ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
 8             callback.GetHourMinuteFromClient();
 9
10         }
11     }

这里实现的方法就是回调客户端的GetHourMinuteFromClient()方法,它是利用OperationContext.Current.GetCallbackChannel 来获取一个实现ICallback的对象,通过该对象则调用指定的回调方法。另外一个就是在ServiceBehavior特性上给ConcurrencyMode属性赋上Reentrant或者Multiple,这样就免得在调用回调方法时出现死锁的异常。

在客户端下载了元数据信息后,就可以实现之前的回调契约,

 1     class ClassCallBack : ILogicCallback
 2     {
 3
 4         public int[] GetHourMinuteFromClient()
 5         {
 6
 7             int[] result= new List<int>() { DateTime.Now.Hour, DateTime.Now.Minute }.ToArray();
 8
 9             Console.WriteLine("{0},{1}",result[0],result[1]);
10
11             return result;
12         }
13     }

不知这里是否与配置有关,鄙人在客户端获取到的接口名称是ILogicCallback,并非与蒋老师的Demo中一样——与服务端的ICallback接口同名。这里只是简单地输出了当前的小时和分钟,并作返回。

最后讲讲配置,能支持双工通讯的binding只有WSDualHttpBinding和NetTcpBinding(自定义的除外),我这里就用了NetTcpBinding。

 1 <system.serviceModel>
 2     <services>
 3       <service name="Logic.LogicService" behaviorConfiguration="te">
 4         <host>
 5           <baseAddresses>
 6             <add baseAddress="net.tcp://127.0.0.1:8004/LogicService"/>
 7           </baseAddresses>
 8         </host>
 9         <endpoint address="" binding="netTcpBinding" contract="Logic.ILogic"  bindingConfiguration="transportNetTcpBinding"/>
10         <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
11       </service>
12     </services>
13     <bindings>
14       <netTcpBinding>
15         <binding name="transportNetTcpBinding" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647">
16           <readerQuotas  maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxDepth="2147483647"
17                         maxNameTableCharCount="2147483647" maxStringContentLength="2147483647"/>
18           <security mode="None"/>
19         </binding>
20       </netTcpBinding>
21     </bindings>
22     <behaviors>
23       <serviceBehaviors>
24         <behavior name="te">
25           <serviceMetadata httpGetEnabled="false"/>
26         </behavior>
27       </serviceBehaviors>
28     </behaviors>
29   </system.serviceModel>

客户端的配置信息鄙人没有自己去写了,直接从服务引用那里粘贴生成的,就可以用得上。

 1 <system.serviceModel>
 2     <bindings>
 3       <netTcpBinding>
 4         <binding name="NetTcpBinding_ILogic">
 5           <security mode="None" />
 6         </binding>
 7       </netTcpBinding>
 8     </bindings>
 9     <client>
10       <endpoint address="net.tcp://127.0.0.1:8004/LogicService" binding="netTcpBinding"
11           bindingConfiguration="NetTcpBinding_ILogic" contract="Proxy.ILogic"
12           name="NetTcpBinding_ILogic" />
13     </client>
14   </system.serviceModel>

在客户端那里调用没有按照蒋老师说介绍的利用通道工厂来创建客户端对象,而是直接利用一个实现了ILogic接口的类来调用,那个类不是自己定义的,也是通过服务引用那里生成得来的。

 1         static void Main(string[] args)
 2         {
 3             InstanceContext context = new InstanceContext(new ClassCallBack());
 4             LogicClient client = new LogicClient(context);
 5             client.Open();
 6                 //using (client as IDisposable)
 7                 //{
 8                     client.ListenToCall();
 9                     //Console.ReadKey();
10                 //}
11             Console.ReadKey();
12         }

蒋老师的博文上有用了using语句块的,但我这里没加上去也行,并没有抛异常,加了上去会更保险吧!

 

  那么鄙人在实际中遇到了这么一个情况,网络通讯已经采用了WCF框架,如果要换技术的话得大动干戈了,想要实现两个客户端之间的通讯,如果像使用Socket实现两个客户端之间直接通讯,那个还比较简单,但是用了WCF之后就比较麻烦了,客户端于客户端之间没法直接通讯,所有交互都是往服务端发请求,调用方法。那么只能使用WCF的双工通讯来实现了,WCF的服务端在整个结构而言就相当于一个中介者

这只是一个通讯的过程,在通讯之前的话肯定是每个客户端都连接一下服务端,让服务端记录了客户端的信息,当有客户端发出与别的客户端通讯的请求时,服务端就会查找出之前记录的信息,调用相应客户端的回调方法来实现。这个过程没分析过其资源的占用情况,但经过实践得出是可行的。

  基于上面的代码,契约里面则需要多增加一个方法,从而使得客户端能往服务端发送取数请求

1     [ServiceContract(CallbackContract = typeof(ICallback))]
2     interface ILogic
3     {
4         [OperationContract]
5         void ListenToCall();
6
7         [OperationContract]
8         List<int> GetHourMinute ();
9     }

回调的契约不需要作改动,实现ILogic的类就改成这样

 1     [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)]
 2     public class LogicService:ILogic
 3     {
 4         protected delegate List<int> callbackDelegate();
 5
 6         private static Dictionary<string, callbackDelegate> clientLst;
 7
 8         protected static Dictionary<string, callbackDelegate> ClientLst
 9         {
10             get
11             {
12                 if (clientLst == null)
13                     clientLst = new Dictionary<string, callbackDelegate>();
14                 return clientLst;
15             }
16         }
17
18         public void ListenToCall()
19         {
20             ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
21             MessageProperties properties = OperationContext.Current.IncomingMessageProperties;
22             RemoteEndpointMessageProperty endpoint =
23                 properties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
24             ClientLst[endpoint.Address+endpoint.Port] = callback.GetHourMinuteFromClient;
25
26         }
27
28
29         public List<int> GetHourMinute ()
30         {
31             //return new List<int>() { DateTime.Now.Hour, DateTime.Now.Minute };
32             List<int> result= ClientLst.First().Value.Invoke();
33
34             return result;
35         }
36     }

ListenToCall的作用就是相当于客户端给服务端报个到,让服务端记录了客户端的IP地址端口号这些信息,还要把回调方法的委托记录下来,这些信息都存在了一个Dictionary<string, callbackDelegate> 的字典集中。在另一个方法GetHourMinute方法里面就负责按照指定的IP地址端口号来调用委托,这样就能调取指定客户端的方法了,不过上面的代码只是一个很基础很基础的示范,很多安全性的判断没加上去。

         本篇博文就此结束了,关于那个客户端间接通讯的有什么意见和建议恳请大家多多提出,鄙人虚心接受,谢谢!

 

时间: 2024-09-05 08:57:32

WCF双工通讯以及客户端间的间接通讯的相关文章

silverlight:wcf双工通讯学习笔记

一直觉得SL中的wcf双工通讯方式有点鸡肋,如果是以http方式实现则效率太低,如果用SL4中的tcp方式实现,又跟socket太雷同,所以一直没去研究,不过这东西在对性能要求不高时(比如在网页上每5分钟更新一次天气预报/股票信息),实现起来还是蛮方便的. wcf双工通讯与传统的wcf相比,最大的区别就是:传统的wcf通常都是客户端去调服务,即客户端从服务端上"拉"信息,而双工通讯除了允许客户端从服务端"拉"信息外,服务端还能主动向客户端"推"送

利用WCF双工模式实现即时通讯_C#教程

概述  WCF陆陆续续也用过多次,但每次都是浅尝辄止,以将够解决问题为王道,这几天稍闲,特寻了些资料看,昨晚尝试使用WCF的双工模式实现了一个简单的即时通讯程序,通过服务端转发实现客户端之间的通讯.这只是个Demo,没有考虑异常处理和性能问题.解决方案结构如下:   契约 using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text;

WCF 双工模式发布在IIS上 客户端引用不了服务端

问题描述 请问大神们一个问题,我做了一个WCF双工模式,我发布在IIS后客户端引用不了,请问是怎么回事呢?这是服务端配置,这是发布IIS,这是客户端引用文件,这是什么原因呢?已经搞了半个月了都没搞定!急!急!急!!!!!!!!! 解决方案 解决方案二:url后面加?wsdl试试总之只要有wsdl描述就可以引用解决方案三:不是那里的问题?解决方案四:自己顶一下!

菜鸟求助WCF双工的问题

问题描述 服务端:[ServiceContract(SessionMode=SessionMode.Required,CallbackContract=typeof(IQQSback))]publicinterfaceIQQS{[OperationContract(IsOneWay=true,IsInitiating=true,IsTerminating=false)]voidSpeak(stringvalue);[OperationContract(IsOneWay=true,IsInitia

[SignalR]SignalR与WCF双工模式结合实现服务端数据直推浏览器端

原文:[SignalR]SignalR与WCF双工模式结合实现服务端数据直推浏览器端 之前开发基于WinForm监控的软件,服务端基于Wcf实现,里面涉及双工模式,在客户端里面,采用心跳包机制保持与服务端链接,现在有了新需求,需要开发网页版形式,所以怎么保持与服务端链接是重要点,由于数据量比较大,所以不能采用客户端发起请求不断轮询的方式.参考各种资料后,使用SignalR,主要是支持WebSockets通信.并且Hub链接方式解决了realtime 信息交换的功能问题. 下图是MSDN关于解释:

应用-微信支付 客户端与微信后台通讯问题-https

问题描述 微信支付 客户端与微信后台通讯问题-https 想做一个PC端应用程序 完成二维码微信支付.采用线下模式.但是由微信开发接口和其C#demo中查看 只提供了HTTPS的传输,请问我改使用怎样的通信模式与微信后台通讯呢?HTTPS是网站的通信协议,本地的客户端程序可以使用吗?怎么使用? 希望可以指点一下,在网络这一块还是小白.多多见谅 解决方案 http://blog.csdn.net/seven_cm/article/details/41559301 解决方案二: 之前我混淆了一个问题

spring-Spring MVC使用hessian客户端与服务器之间通讯步骤,求教详细过程!谢谢

问题描述 Spring MVC使用hessian客户端与服务器之间通讯步骤,求教详细过程!谢谢 Spring MVC使用hessian客户端与服务器之间通讯步骤,求教详细过程!谢谢 解决方案 http://www.blogjava.net/iduido/archive/2012/04/11/373831.html

服务器-急求c++builder服务端和客户端间收发70M大小的图片,怎么实现???

问题描述 急求c++builder服务端和客户端间收发70M大小的图片,怎么实现??? 用TMemoryStream会out of memory,应该实现? 解决方案 分割成小块,传输的时候先传文件大小的整数,客户端开辟一个这么大的文件. 然后依次传输每一个数据块,比如128K.客户端移动文件指针,把数据块写入指定位置. http://download.csdn.net/download/future_ok/4090832 解决方案二: java实现Thrift服务端和客户端

mdk5-观察到一个现象:usb HID设备在成功枚举完了之后就与主机不通讯了,怎么重新开始通讯呢?

问题描述 观察到一个现象:usb HID设备在成功枚举完了之后就与主机不通讯了,怎么重新开始通讯呢? stm32f103zet6,开发环境mdk517,用的是st官方给的hid的键盘的例程,usb能成功被枚举,而且显示有一个标准的输入设备,但是我再debug模式下起初在枚举阶段我能看到在中断函数中设置的中断点出停下来,说明主机给设备发了一些东西,但是当主机成功枚举之后,pc上显示这个设备运转正常,这个时候再在中断函数中任何地方设置断点都没有作用,也就是说这时主机就没有给设备发in包,那岂不是直接