• 你来我往:wcf的双向通信(Bidirectional Communication)


    我们继续改进前面的示例,实现wcf的双向通信。
    示例采用前文中一样的业务逻辑(单独针对DivideOperation进行说明,另外两个方法我们注释掉不再讨论)——调用一个数学除法计算的远程调用,除了传递相应的操作参数之外,我们还传递一个对象,这个对象可以在Server端中回调 (Callback) 把运算结果在Client端显示出来。
    我们来看一下改进的步骤和实现:
    1、IGetDataCallback.cs
    Code
    2、IGetDataService.cs
    Code
    注意点:

    (1).在一个分布式的环境中,Client能够调用Service,它必须知道Service的Contract, Contract定义了Service暴露给外界的所有可用的Operation,以及这些Operation的签名(Signature).至于Service中定义的Opertion采用怎样的实现,Client不需要了解。这也是在WCF中把contract契约与具体的实现相互分离的一个重要原因——我们把Contract单独提取出来,把他暴露给Client,从而可以避免把过多的暴露业务逻辑的实现。
    (2).在一个分布式的环境中,Server端和Client并不是一成不变的,他们是可以相互转化的。提供服务的就是Server,消费Service的就是Client。在这个例子中,当MyClient调用Host在MyWcfWebHost中的GetDataService(定义在MyWcfLib类库中),MyClient是Client,而Server端的执行环境是MyWcfWebHost。而当GetDataService回调(Callback)Client的逻辑把运算结果显示出来时候,因为Callback的逻辑是MyClient中执行的,所以MyClient成了Server,而GetDataCallbackHandler(定义在MyClient中)成了真正的Service。
    (3).我们已经说过Client能够调用Service,它必须知道Service的Contract。所以GetDataService能过Callback MyClient,它也必须知道回调操作的Contract。WCF通过在ServiceContractAttribute中的CallbackContrac参数制定。
    2、在GetDataService中定义服务:

    Code

    需要注意:在客户端的app.config文件我们把binding设置为"wsDualHttpBinding",也就是可以双向通信的绑定。您还可以设置其他的双向绑定。
    3、客户端的通信测试
    (1)、callback调用的服务

    代码
        /// <summary>
        
    /// 实现回调接口
        
    /// 在callBack的时候调用的服务,这个服务应该由client程序实现
        
    /// </summary>
        public class ClientGetDataCallback : WcfContract.IGetDataCallback
        {
            
    public void ShowResult(double x, double y, double z)
            {
                
    string result = string.Format("{0} divide {1} equals {2}", x, y, z);
                Console.WriteLine(result);
            }
        }

     (2)、双向通信测试代码
    我们依然在一个控制台应用程序中进行简单测试:

    代码
        class Program
        {
            
    static void Main(string[] args)
            {
                InstanceContext instanceContext 
    = new InstanceContext(new ClientGetDataCallback());
                DuplexChannelFactory
    <IGetDataService> channelFactory = new DuplexChannelFactory<IGetDataService>(instanceContext, "MyWcfGetDataService");
                IGetDataService proxy 
    = channelFactory.CreateChannel();//获取指定类型的一个通道
                using (proxy as IDisposable)
                {
                    
    try
                    {
                        proxy.DivideOperation(
    255);
                        proxy.DivideOperation(
    2515);
                        Console.ReadKey();
    //如果这一行注释掉,异常:在消息传输完成以前关闭会话
                    }
                    
    catch (FaultException<CommonError> ex)   //异常处理
                    {
                        CommonError error 
    = ex.Detail;
                        Console.WriteLine(
    "An Fault is thrown.\n\tFault code:{0}\n\tFault Reason:{1}\n\tOperation:{2}\n\tMessage:{3}", ex.Code, ex.Reason, error.Operation, error.ErrorMessage);
                        
    //写异常日志
                    }
                    
    catch (Exception ex)
                    {
                        Console.WriteLine(
    "An Exception is thrown.\n\tException Type:{0}\n\tError Message:{1}", ex.GetType(), ex.Message);
                        
    //写异常日志
                    }
                }
                Console.WriteLine(
    "\r\nIt is a wcf test and succeed now!");
                Console.ReadKey();
            }
        }

     4、注意点
    测试时发现的两个异常:
    (1)、“HTTP 无法注册 URL...,因为另一应用程序正在使用 TCP 端口 80。”
    施展google大法后发现这是xp系统或者iis5.x的常见问题,您可以直白地把它理解为“tmd,老子的回调服务的监听端口80被占用啦”,所以解决的方法就是,

    代码
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      
    <system.serviceModel>
        
    <bindings>
          
    <wsDualHttpBinding>
            
    <!--xp系统或者iis 5.x 必须自定义这一个ClientBaseAddress:80和我们已经设定好的4321端口不能作为回调服务的基地址端口-->
            
    <binding name="WSDualHttpBinding_IGetDataService" clientBaseAddress="http://localhost:1234/GetData" />
          
    </wsDualHttpBinding>
        
    </bindings>
        
    <client>
          
    <endpoint address="http://localhost:4321/GetData/GetData.svc"
            binding
    ="wsDualHttpBinding" bindingConfiguration="WSDualHttpBinding_IGetDataService"
            contract
    ="WcfContract.IGetDataService" name="MyWcfGetDataService">
          
    </endpoint>
        
    </client>
      
    </system.serviceModel>
    </configuration>

     正如你所看见的,我们独立为它配置一个不同的双向绑定的clientBaseAddress的端口就可以了。
    (2)、“在消息传输完成以前关闭会话”
    这个问题是在测试的时候发现的,我在测试代码里也写了注释。分析过后发现,原来我们将proxy转换为一个IDisposable的对象后(using (proxy as IDisposable)这一句),在测试代码的try块内,如果没有 Console.ReadKey();这一行,clr会调用Dispose()方法进行回收,试图关闭底层的用于双向通信的信道,如果在回调操作尚未执行完毕就关闭网络连接,将会导致回调无法正常执行。
    demo下载:demo

  • 相关阅读:
    前端常用模板引擎- artTemplate
    Vue-多级组件嵌套传值
    echarts图表常用到的设置
    react-基础入门分享
    vue中 export const 和 export default的区别
    vue安装依赖报错
    nvm-node版本控制工具
    gulp-入门
    vue 中使用 iconfont
    c3中基本动画
  • 原文地址:https://www.cnblogs.com/jeffwongishandsome/p/1542850.html
Copyright © 2020-2023  润新知