- 使用管理员权限打开VS2017
2. 创建一个空白解决方案:
3. 创建几个基于.net framework的类库项目和一个基于.net Framework 的控制台项目, 然后将类库项目的class1.cs文件删除, 整体如下图:
4. 为每个类库添加对应的类文件:
a) MyWCF.WCFSimpleDuplex.Interface添加两个接口, ICalculatorService和ICallBack, 其中ICalculatorService表示要启动的服务接口, 而ICallBack表示要回调客户端时, 访问的接口
b) ICalculatorService 代码如下:
using System.ServiceModel; namespace MyWCF.WCFSimpleDuplex.Interface { /// <summary> /// 定义协议的时候, 同时需要定义一个回调接口 /// 示例中定义服务契约的同时定义一个回调接口ICallBack /// 双工中的函数不能带返回值 /// 如果你实在想要返回值, 那么自己用out /// </summary> [ServiceContract(CallbackContract = typeof(ICallBack))] public interface ICalculatorService { /// <summary> /// 添加isoneway属性表示, 这个函数是不是返回应答信息 ; /// </summary> /// <param name="x"></param> /// <param name="y"></param> [OperationContract(IsOneWay = true)] void Plus(int x, int y); //建立了一个Icalculator服务的协议 //这个协议带有一个回调接口ICallBack //注意WCF的双工, 不能全靠配置, 在建立服务的时候, 就要开始处理了 } }
c) ICallBack代码如下
using System.ServiceModel; namespace MyWCF.WCFSimpleDuplex.Interface { /// <summary> /// 不需要协议, 因为它不是一个服务契约, 它只是一个约束,这个约束由客户端实现 /// </summary> public interface ICallBack { /// <summary> /// 这里就是回调的时候完成的函数, 这个接口也要加上Isoneway=true /// </summary> /// <param name="m"></param> /// <param name="n"></param> /// <param name="result"></param> [OperationContract(IsOneWay = true)] void Show(int m, int n, int result); } //要发布一个双工协议 ,首先在服务的建立WCF服务, 服务的契约上面指定callbackContract回调的类型 //建立回调接口, 回调接口就是等下要执行的接口 }
5. 接下来处理 MyWCF.WCFSimpleDuplex.Model 的代码, 它就一个类:
using System.Runtime.Serialization; namespace MyWCF.WCFSimpleDuplex.Model { [DataContract] public class WCFUser { //[DataMember] public int Id { get; set; } [DataMember] public int Age { get; set; } [DataMember] public int Sex { get; set; } [DataMember] public string Name { get; set; } [DataMember] public string Description { get; set; } } public enum WCFUserSex { Famale, Male, Other } }
6. 关于服务类MyWCF.WCFSimpleDuplex.Service的处理如下:
using System.ServiceModel; using MyWCF.WCFSimpleDuplex.Interface; namespace MyWCF.WCFSimpleDuplex.Service { /// <summary> /// 对ICalculatorService的一个实现类; 实现的时候, 除了要完成自己的计算, 还要完成, 对接口的回调 /// </summary> public class CalculatorService : ICalculatorService { /// <summary> /// 完成计算,然后去回调 /// </summary> /// <param name="x"></param> /// <param name="y"></param> public void Plus(int x, int y) { int result = x + y; System.Threading.Thread.Sleep(3000); //OperationContext表示当前操作的上下文 //OperationContext.Current.GetCallbackChannel<ICallBack>();这里有点像IOC, 给你一个接口然后生成一个实例; 注意哦, 这里的ICallBack还有没有任何实现, 而这个实现是在客户端(client)处去完成的 ICallBack callBack =OperationContext.Current.GetCallbackChannel<ICallBack>(); callBack.Show(x, y, result); } } }
7. 接下来是控制台MyWCF.WCFSimpleDuplex.ConsoleTest的代码实现, 首先是他的配置文件
a) 初始时的配置文件如下图:
b) 在configuration节点添加如下配置:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <system.serviceModel> <!--WCF的配置文件 , WCF的配置文件一个是基于TCP的, 一个是基于http的; 可以添加多个; 如果添加多个就是下面的配置格式 --> <behaviors> <serviceBehaviors> <!--<behavior name="MathServicebehavior"> --><!--这里配置第一个Behavior的节点--><!-- <serviceDebug httpHelpPageEnabled="false"/> <serviceMetadata httpGetEnabled="false"/> <serviceTimeouts transactionTimeout="00:10:00"/> <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/> </behavior>--> <behavior name="CalculatorServicebehavior"> <!--这里配置第二个Behavior的节点--> <serviceDebug httpHelpPageEnabled="false"/> <serviceMetadata httpGetEnabled="false"/> <serviceTimeouts transactionTimeout="00:10:00"/> <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/> </behavior> </serviceBehaviors> </behaviors> <!--在tcp中上面几行和http的配置都是一样的--> <bindings> <netTcpBinding> <binding name="tcpbinding"> <!--指定为tcp的协议类型--> <security mode="None"> <!--clientCredentialType="None" → 表示加密类型 protectionLevel="None" 表示安全级别--> <transport clientCredentialType="None" protectionLevel="None"/> </security> </binding> </netTcpBinding> </bindings> <services> <service name="MyWCF.WCFSimpleDuplex.Service.CalculatorService" behaviorConfiguration="CalculatorServicebehavior"> <host> <!--这里配置第一个服务的名称和服务的具体指向类--> <baseAddresses> <add baseAddress="net.tcp://localhost:9999/CalculatorService"/><!--指定的协议为net tcp协议--> </baseAddresses> </host> <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="MyWCF.WCFSimpleDuplex.Interface.ICalculatorService"/> <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/> </service> <!--<service name="MyWCF.WCFSimpleDuplex.Service.MathService" behaviorConfiguration="MathServicebehavior"> <host>--><!--这里配置第二个服务的名称和服务的具体指向类--><!-- <baseAddresses> <add baseAddress="net.tcp://localhost:9999/MathService"/> </baseAddresses> </host> <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="MyWCF.WCFSimpleDuplex.Interface.IMathService"/> <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/> </service>--> </services> </system.serviceModel> </configuration>
c) Program代码如下:
using System; using System.ServiceModel; using MyWCF.WCFSimpleDuplex.Service; namespace MyWCF.WCFSimpleDuplex.ConsoleTest { class Program { static void Main(string[] args) { try { Console.WriteLine("开始挂起服务"); ServiceInit.Process(); } catch (Exception ex)//如果报无法注册. . ., 则说明需要管理员权限, 启动这个程序 { //服务“SOA.WCF.Service.CalculatorService”有零个应用程序(非基础结构)终结点。这可能是因为未找到应用程序的配置文件,或者在配置文件中未找到与服务名称匹配的服务元素,或者服务元素中未定义终结点。 //没有配置文件 //另一应用程序已使用 HTTP.SYS 注册了该 URL。 //端口 9999 被其它应用程序占用了, 找到并将其停止 Console.WriteLine(ex.Message); } Console.Read(); } } /// <summary> /// WCF寄宿到控制台 /// </summary> public class ServiceInit { public static void Process() { //ServiceHost →表示提供服务的主机,使用服务的类型及其指定的基址初始化 System.ServiceModel.ServiceHost 类的新实例。 Type type = typeof(CalculatorService); ServiceHost host = new ServiceHost(type); host.Opening += (s, e) => Console.WriteLine($"{type} 准备打开"); //已经打开事件 host.Opened += (s, e) => Console.WriteLine($"{type} 已经正常打开"); //开启服务; 注意当这里需要Open的时候, 会去配置文件中检查是否有配置了MathService这个类的behavior(行为) host.Open(); Console.WriteLine("输入任何字符,就停止"); Console.Read(); host.Close(); Console.Read(); } } }
8. 完成以上步骤就可以启动试试看了:
9. 可以使用 cmd 命令 netstat -ano | find "9999", 查看下端口9999是否被监听
10. 双工的服务端已经处理完毕, 接下来开始处理客户端, 当然客户端首先就要对刚才的ICallBack进行实现, 这个毋容置疑的
11. 再新打开一个VS2017, 创建一个控制台测试项目:
12. 接下来不用多想, 第一件事, 添加服务引用, 第二件事实现服务端的那个ICallBack接口
a) 添加服务引用
b) 实现ICallBack接口, 注意这个类里的Show 方法, 在客户端没有任何代码调用
using System; namespace MyWCF.WCFSimpleDuplexConsoleTest.CTest { /// <summary> /// 具体实现的回调 /// </summary> public class CallBackService : MyDuplexConsoleTest.ICalculatorServiceCallback { public void Show(int m, int n, int result) { Console.WriteLine($"回调操作展示, 这个操作发生在两秒之后:{m}+{n}={result}"); } } }
c) 整体client截图:
13. 然后在program中的代码, 建议单步调试看看代码的运行过程:
using System; using System.ServiceModel; namespace MyWCF.WCFSimpleDuplexConsoleTest.CTest { class Program { static void Main(string[] args) { MyDuplexConsoleTest.CalculatorServiceClient client = null; try { Console.WriteLine("客户端来测试双工!~~"); //创建一个需要回调的实例, 等会服务端进行回调的 InstanceContext context = new InstanceContext(new CallBackService()); client = new MyDuplexConsoleTest.CalculatorServiceClient(context); client.Plus(123, 234); client.Close(); } catch (Exception ex) { if (client != null) client.Abort(); Console.WriteLine(ex.Message); } Console.Read(); } } }
14. 结果截图
15. 结束