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证书
  • 相关阅读:
    (双指针 二分) leetcode 167. Two Sum II
    (双指针) leetcode 485. Max Consecutive Ones
    (双指针) leetcode 27. Remove Element
    (String) leetcode 67. Add Binary
    (数组) leetcode 66. Plus One
    (N叉树 BFS) leetcode429. N-ary Tree Level Order Traversal
    (N叉树 递归) leetcode 590. N-ary Tree Postorder Traversal
    (N叉树 递归) leetcode589. N-ary Tree Preorder Traversal
    (N叉树 DFS 递归 BFS) leetcode 559. Maximum Depth of N-ary Tree
    (BST 递归) leetcode98. Validate Binary Search Tree
  • 原文地址:https://www.cnblogs.com/jesen1315/p/12130904.html
Copyright © 2011-2022 走看看