wcf 双向 java_你来我往:wcf的双向通信(Bi-directional Communication)

本文详细介绍了WCF的双向通信机制,通过一个数学除法运算的示例,展示了如何在服务端定义回调接口并在客户端实现。重点讨论了服务合同、服务行为、客户端回调接口的实现以及测试过程中的注意事项,包括端口冲突和会话关闭问题的解决策略。
摘要由CSDN通过智能技术生成

我们继续改进前面的示例,实现wcf的双向通信。

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

1、IGetDataCallback.cs

9310e85a14af99de4811ff4c77f1f911.png

24a924a57ba6b3f2b51fc9edb7ea4186.pngCodeusingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.ServiceModel;namespaceWcfContract

{

[ServiceContract]interfaceIGetDataCallback

{

[OperationContract]stringShowResult(doublex,doubley,doubleresult);

}

}2、IGetDataService.cs

9310e85a14af99de4811ff4c77f1f911.png

24a924a57ba6b3f2b51fc9edb7ea4186.pngCodeusingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Runtime.Serialization;usingSystem.ServiceModel;usingSystem.Text;usingMyWcfException;namespaceWcfContract

{

[ServiceContract(CallbackContract=typeof(IGetDataCallback))]publicinterfaceIGetDataService

{

[OperationContract]

[FaultContract(typeof(CommonError))]doubleDivideOperation(doublex,doubley);

}

}注意点:

(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中定义服务:

9310e85a14af99de4811ff4c77f1f911.png

24a924a57ba6b3f2b51fc9edb7ea4186.pngCodeusingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingWcfContract;usingMyWcfException;usingSystem.ServiceModel;namespaceMyWcfLib

{

[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)]publicclassGetDataService : IGetDataService

{publicdoubleDivideOperation(doublex,doubley)

{//声明回调接口IGetDataCallback callback=OperationContext.Current.GetCallbackChannel();//在被除数为0的时候,抛出FaultException Exception,并指定具体的CommonError对象//以及一个FaultCode(一般指明出错的来源)和FaultReason(出错的原因)if(y==0)

{

CommonError error=newCommonError("Divide","Divided by zero");thrownewFaultException(error,newFaultReason("Parameters passed are not valid"),newFaultCode("sender"));

}//调用回调接口中的方法callback.ShowResult(x, y, x/y);returnx/y;

}

}

}

需要注意:在客户端的app.config文件我们把binding设置为"wsDualHttpBinding",也就是可以双向通信的绑定。您还可以设置其他的双向绑定。

3、客户端的通信测试

(1)、callback调用的服务

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png代码

//实现回调接口///在callBack的时候调用的服务,这个服务应该由client程序实现///publicclassClientGetDataCallback : WcfContract.IGetDataCallback

{publicvoidShowResult(doublex,doubley,doublez)

{stringresult=string.Format("{0} divide {1} equals {2}", x, y, z);

Console.WriteLine(result);

}

}

(2)、双向通信测试代码

我们依然在一个控制台应用程序中进行简单测试:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png代码

classProgram

{staticvoidMain(string[] args)

{

InstanceContext instanceContext=newInstanceContext(newClientGetDataCallback());

DuplexChannelFactorychannelFactory=newDuplexChannelFactory(instanceContext,"MyWcfGetDataService");

IGetDataService proxy=channelFactory.CreateChannel();//获取指定类型的一个通道using(proxyasIDisposable)

{try{

proxy.DivideOperation(25,5);

proxy.DivideOperation(25,15);

Console.ReadKey();//如果这一行注释掉,异常:在消息传输完成以前关闭会话}catch(FaultExceptionex)//异常处理{

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被占用啦”,所以解决的方法就是,

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png代码

正如你所看见的,我们独立为它配置一个不同的双向绑定的clientBaseAddress的端口就可以了。

(2)、“在消息传输完成以前关闭会话”

这个问题是在测试的时候发现的,我在测试代码里也写了注释。分析过后发现,原来我们将proxy转换为一个IDisposable的对象后(using (proxy as IDisposable)这一句),在测试代码的try块内,如果没有 Console.ReadKey();这一行,clr会调用Dispose()方法进行回收,试图关闭底层的用于双向通信的信道,如果在回调操作尚未执行完毕就关闭网络连接,将会导致回调无法正常执行。

demo下载:demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值