zoukankan      html  css  js  c++  java
  • .Net中关于SOA的三大组件之WCF

      上一篇介绍了WebService,想想也是刚毕业那时候进入公司就接触的WebService,所以也相对熟悉一些。而WCF本人在实际应用中也很少使用,但平常的时候也有所了解。

      WCF是.NetFramework3.5推出的,被称之为分布式服务的集大成者。它不像WebService只能寄宿在IIS上,它支持不同的协议,比如http、tcp、 IPC、 MSMQ、 p2p,并且支持不同的宿主:网站、控制台、winform、WindowsService。同时也可以支持双工通信。

      当我们在VS上创建一个WCF项目时,其为我们生成Service1.svc和IService1.cs接口。下面来看一下一个WCF方法需要哪些东西吧。

    // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IService1”。
    [ServiceContract]
    public interface IService1
    {
    
        [OperationContract]
        string GetData(int value);
    
        [OperationContract]
        CompositeType GetDataUsingDataContract(CompositeType composite);
    
        // TODO: 在此添加您的服务操作
    }
    
    
    // 使用下面示例中说明的数据约定将复合类型添加到服务操作。
    [DataContract]
    public class CompositeType
    {
        bool boolValue = true;
        string stringValue = "Hello ";
    
        [DataMember]
        public bool BoolValue
        {
            get { return boolValue; }
            set { boolValue = value; }
        }
    
        [DataMember]
        public string StringValue
        {
            get { return stringValue; }
            set { stringValue = value; }
        }
    }

      上面代码是我们新建项目时为我们生成的,从中可以窥探一二。

    • 首先WCF需要一个抽象接口,该接口需要添加特性:ServiceContract
    • 接口里面的方法需要添加特性OperationContract才可以被调用
    • 实体具有无参数构造函数时,可以不标记DataContract特性,调用时会将数据全部返回
    • 实体没有无参数构造函数时,需要添加DataContract,当实体类上面有DataContract时,那么类中成员变量需要添加[DataMember],否则不可见
    • 真实工作中,都会带上DataContract 和 DataMember

       我们知道,将WCF应用部署到IIS上很简单,这里就不多说了。下面看一下将WCF通过TCP的方式托管在控制台或Winform或WindowsService上怎么做。其实也很简单,也是通过配置文件来配置,然后在程序中使用System.ServiceModel.ServiceHost来开启服务(注意权限问题)。

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
      </startup>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="MathServicebehavior">
              <serviceDebug httpHelpPageEnabled="false"/>
              <serviceMetadata httpGetEnabled="false"/>
              <serviceTimeouts transactionTimeout="00:10:00"/>
              <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/>
            </behavior>
    
            <behavior name="CalculatorServicebehavior">
              <serviceDebug httpHelpPageEnabled="false"/>
              <serviceMetadata httpGetEnabled="false"/>
              <serviceTimeouts transactionTimeout="00:10:00"/>
              <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
    
        <bindings>
          <netTcpBinding>
            <binding name="tcpbinding">
              <security mode="None">
                <transport clientCredentialType="None" protectionLevel="None"/>
              </security>
            </binding>
          </netTcpBinding>
        </bindings>
        <services>
          <service name="SOA.WCF.Service.CalculatorService" behaviorConfiguration="CalculatorServicebehavior">
            <host>
              <baseAddresses>
                <add baseAddress="net.tcp://localhost:11111/CalculatorService"/>
              </baseAddresses>
            </host>
            <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="SOA.WCF.Interface.ICalculatorService"/>
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
          </service>
    
          <service name="SOA.WCF.Service.MathService" behaviorConfiguration="MathServicebehavior">
            <host>
              <baseAddresses>
                <add baseAddress="net.tcp://localhost:11111/MathService"/>
              </baseAddresses>
            </host>
            <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="SOA.WCF.Interface.IMathService"/>
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
          </service>
        </services>
    
        <!--<behaviors>
          <serviceBehaviors>
            <behavior name="MathServicebehavior">
              <serviceDebug httpHelpPageEnabled="false"/>
              <serviceMetadata httpGetEnabled="false"/>
              <serviceTimeouts transactionTimeout="00:10:00"/>
              <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        
        <bindings>
          <basicHttpBinding>
            <binding name="httpbinding"/>
          </basicHttpBinding>
        </bindings>
        
        <services>
          <service name="SOA.WCF.Service.MathService" behaviorConfiguration="MathServicebehavior">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:11113/MathService"/>
              </baseAddresses>
            </host>
            <endpoint address="" binding="basicHttpBinding" bindingConfiguration="httpbinding" contract="SOA.WCF.Interface.IMathService"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
          </service>
        </services>-->
    
      </system.serviceModel>
    </configuration>
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.ServiceModel;
    using SOA.WCF.Service;
    using SOA.WCF.Interface;
    using System.ServiceModel.Description;
    
    namespace SOA.Project
    {
        public class ServiceInit
        {
            public static void Process()
            {
                //ServiceHost对象
                List<ServiceHost> hosts = new List<ServiceHost>()
                {
                    new ServiceHost(typeof(MathService)),
                    new ServiceHost(typeof(CalculatorService))
                };
                foreach (var host in hosts)
                {
                    host.Opening += (s, e) => Console.WriteLine($"{host.GetType().Name} 准备打开");
                    host.Opened += (s, e) => Console.WriteLine($"{host.GetType().Name} 已经正常打开");
                    host.Closing += (s, e) => Console.WriteLine($"{host.GetType().Name} 准备关闭");
                    host.Closed += (s, e) => Console.WriteLine($"{host.GetType().Name} 准备关闭");
    
                    host.Open();
                }
                Console.WriteLine("输入任何字符,就停止");
                Console.Read();
                foreach (var host in hosts)
                {
                    host.Close();
                }
                Console.Read();
    
    
                #region MyRegion
                //using (ServiceHost host = new ServiceHost(typeof(MathService)))
                //{
                //    #region 程序配置
                //    host.AddServiceEndpoint(typeof(IMathService), new WSHttpBinding(), "http://127.0.0.1:9999/calculatorservice");
                //    if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
                //    {
                //        ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                //        behavior.HttpGetEnabled = true;
                //        behavior.HttpGetUrl = new Uri("http://127.0.0.1:9999/calculatorservice/metadata");
                //        host.Description.Behaviors.Add(behavior);
                //    }
                //    #endregion 程序配置
                //    host.Opened += (s, e) =>
                //    {
                //        Console.WriteLine("MathService已经启动,按任意键终止服务!");
                //    };
    
                //    host.Open();
                //    Console.Read();
                //}
    
                #endregion
            }
    
            private static void Host_Closing(object sender, EventArgs e)
            {
                throw new NotImplementedException();
            }
        }
    }

       那为什么采用http方式部署到IIS那么简单,我们还要使用其他协议来部署呢?这个当然有各个协议的优点和具体的用处了,例如TCP协议速度快点,但是不能穿透防火墙,可是其是有状态的而http是无状态的。而且通常WCF会和WindowsService结合被用来做定时调度任务,关于任务调度可以看我这篇博文 :https://www.cnblogs.com/jesen1315/p/11653181.html 。

       因为WCF可以采用TCP、Pipeline协议,所以WCF还可以做双工通信,其核心思路就是回调,具体做法:

    • 在服务接口上约定,CallbackContract(回调接口)
    • 回调接口里面方法需要IsOneWay=true
    • 启动服务,客户端引用服务,实现回调接口
    • 调用服务时,讲回调方法传递进去
    • 不仅是客户端可以向服务端发消息,而且服务端可以主动向客户端发消息

       下面我们来看下具体的代码

    [ServiceContract(CallbackContract = typeof(ICallBack))]
    public interface ICalculatorService
    {
        [OperationContract(IsOneWay = true)]
        void Plus(int x, int y);
    }
    
    /// <summary>
    /// 不需要协议  是个约束,由客户端实现
    /// </summary>
    public interface ICallBack
    {
        [OperationContract(IsOneWay = true)]
        void Show(int m, int n, int result);
    }
    
    
    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;
            Thread.Sleep(2000);
            ICallBack callBack = OperationContext.Current.GetCallbackChannel<ICallBack>();
            callBack.Show(x, y, result);
        }
    }

      最后关于WCF的安全,通常WCF一般是给后端用的,也是在内网中使用,但是也不排除有些公司提供给第三方的接口采用的是WCF服务,像我现在公司就是,所以也要注意到其安全性。那么关于WCF的一般可以有以下的方式,具体怎么做就不多说了。

    • 没有身份验证--局域网后台调用
    • 发布口令--请求式加个参数(事先约定)
    • soapheader--用户的账号密码
    • Windows 身份验证
    • X509证书
  • 相关阅读:
    第3.2节 Python列表简介
    新闻发布项目——Servlet类(doNews_readServlet )
    新闻发布项目——业务逻辑层(commentServiceImpl)
    新闻发布项目——业务逻辑层(commentServiceImpl)
    新闻发布项目——业务逻辑层(commentServiceImpl)
    新闻发布项目——业务逻辑层(categoryTBServiceImpl)
    新闻发布项目——业务逻辑层(categoryTBServiceImpl)
    新闻发布项目——业务逻辑层(categoryTBServiceImpl)
    新闻发布项目——业务逻辑层(newsTbServiceImpl)
    新闻发布项目——业务逻辑层(newsTbServiceImpl)
  • 原文地址:https://www.cnblogs.com/jesen1315/p/12130904.html
Copyright © 2011-2022 走看看