C# WCF服务入门-程序员宅基地

技术标签: 面试  网络  操作系统  

之前在公司用的服务端是wcf写的,但是没有深入研究,最近找工作,面试的时候好多人看到这个总提问,这里做个复习

就用微软官方上的例子,搭一个简单的wcf服务,分6步

1 定义服务协定也就是契约,其实就是定义一个服务接口,这玩意后边是公开客户端用的,然后也告诉后边承载程序应该如何加载服务

   主要涉及两个特性:一个是ServiceContract(接口的特性,定义这个是服务契约,里边又一些设置参数可以设置一下),OperationContract设置接口的方法的,如果不设置,方法就不会呗公开

  这里是直接新建的wcf的服务程序,vs自动给生成了一个接口,就直接在这个里边添加了几个计算的接口函数了

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace GettingStartedLib
{

    // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IService1”。
    //协定
    [ServiceContract(//CallbackContract =typeof(ICallBack),//双工时的返回协定
        ConfigurationName = "Calculator",//配置文件重的服务名
        Name = "ICalculator",//webservice描述文件重的portType名
        Namespace ="http://SampleWcfTest",//webservice描述文件重的portType命名空间
        ProtectionLevel =System.Net.Security.ProtectionLevel.EncryptAndSign,//保护等级
        SessionMode =SessionMode.Allowed)]//设置会话的支持模式
    public interface ICalculator
    {

        [OperationContract]
        string GetData(int value);

        [OperationContract]
        CompositeType GetDataUsingDataContract(CompositeType composite);
        //定义方法的操作,带了该特性才会被公布
        [OperationContract]
        double Add(double n1, double n2);
        [OperationContract]
        double Subtract(double n1, double n2);
        [OperationContract]
        double Multiply(double n1, double n2);
        [OperationContract]
        double Divide(double n1, double n2);

        // TODO: 在此添加您的服务操作
    }
    public interface ICallBack
    {
        [OperationContract(IsOneWay = true)]
        void Reply(string responseToGreeting);
    }

    // 使用下面示例中说明的数据约定将复合类型添加到服务操作。
    [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; }
        }
    }
}

2 实现上边的接口,vs自动生成了svc文件和对应的svc.cs文件,直接双击就是对应的实现类了,吧接口实现就好了,这个svc文件右键用文本编辑器打开可以进行编辑,如果后边用IIS加载的时候需要修改里边的东西

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace GettingStartedLib
{
    // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“Service1”。
    // 注意: 为了启动 WCF 测试客户端以测试此服务,请在解决方案资源管理器中选择 Service1.svc 或 Service1.svc.cs,然后开始调试。
    public class Calculator : ICalculator
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }

        public CompositeType GetDataUsingDataContract(CompositeType composite)
        {
            if (composite == null)
            {
                throw new ArgumentNullException("composite");
            }
            if (composite.BoolValue)
            {
                composite.StringValue += "Suffix";
            }
            return composite;
        }
        public double Add(double n1, double n2)
        {
            double result = n1 + n2;
            Console.WriteLine("Received Add({0},{1})", n1, n2);
            // Code added to write output to the console window.
            Console.WriteLine("Return: {0}", result);
            return result;
        }

        public double Subtract(double n1, double n2)
        {
            double result = n1 - n2;
            Console.WriteLine("Received Subtract({0},{1})", n1, n2);
            Console.WriteLine("Return: {0}", result);
            return result;
        }

        public double Multiply(double n1, double n2)
        {
            double result = n1 * n2;
            Console.WriteLine("Received Multiply({0},{1})", n1, n2);
            Console.WriteLine("Return: {0}", result);
            return result;
        }

        public double Divide(double n1, double n2)
        {
            double result = n1 / n2;
            Console.WriteLine("Received Divide({0},{1})", n1, n2);
            Console.WriteLine("Return: {0}", result);
            return result;
        }
    }
}

3 承载服务,承载服务有几种方式,同时又设计到协议的绑定问题,

  绑定分类:BasicHttpBinding  最基础的http绑定,
        NetTcpbingding  TCP的  

        NetNamePipeBinding  IPC,也就是进程间通信,没用过。。

        WSHttpBinding  这个是特殊的http/Https协议 加了其他东西的

        NetMsmqBindiing,这个是消息队列,我没用过,这玩意好像需要额外装微软的消息队列才可以用

  这里我基本就用wshttpbinding来实现,

  承载方式:1)用控制台程序,直接用servicehost来承载服务,自己构造终结点:

            //服务的地址
            Uri baseAddress = new Uri("http://localhost:8000/GettingStarted/");
            //承载服务的宿主
            ServiceHost selfHost = new ServiceHost(typeof(Calculator), baseAddress);

            try
            {
                selfHost.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "Calculator");
                ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
                smb.HttpGetEnabled = true;
                selfHost.Description.Behaviors.Add(smb);
                selfHost.Open();
                Console.WriteLine("The service is ready.");
                Console.WriteLine("input<exit> to terminate service.");
                Console.WriteLine();
                while ("exit" == Console.ReadLine())
                {
                    selfHost.Close();
                }
            }
            catch (CommunicationException ex)
            {
                Console.WriteLine(ex.Message);
                selfHost.Abort();
                Console.ReadLine();
            }

    2) 用IIS承载,这里又两种方式,通过internet的方式 这个可以直接在vs中调试的,选中svc文件点调试会弹出一个wcf的客户端测试程序,可以直接进行测试,但是这个客户端不能测试异步,只能测同步

    这中方式主要是需要配置一个web.config文件,部署的话,直接在IIS添加网站服务程序,把生成的DLL和web.config放在一个目录下应该跟这个调试是一样的(这种方式IIS5 6 只支持HTTP)

    还可以通过WAS(进程激活服务,支持的协议比上边多,这个就没有深入研究了···)

<?xml version="1.0" encoding="utf-8"?>
<configuration>

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5.2" />
    <httpRuntime targetFramework="4.5.2"/>
  </system.web>
  <system.serviceModel>
    <services>
      <service name="GettingStartedLib.Calculator" behaviorConfiguration="MyServiceTypeBehaviors" >
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8001/"/>
          </baseAddresses>
        </host>
        <endpoint address="CalculatorService" binding="wsHttpBinding" contract="Calculator">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyServiceTypeBehaviors" >
          <!-- 将下列元素添加到服务行为配置中。 -->
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
  <system.webServer>
    <!--<modules runAllManagedModulesForAllRequests="true"/>-->
    <!--
        若要在调试过程中浏览 Web 应用程序根目录,请将下面的值设置为 True。
        在部署之前将该值设置为 False 可避免泄露 Web 应用程序文件夹信息。
      -->
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>

  3)通过windows服务程序承载,这个内部起始也是servicehost来实现,只不过是挂在windows服务程序上,需要自己实现一个windows服务,也就是继承servicebase类,重写onstart,在onstart中用servicehost加载服务,在stop中停止服务,同时需要配置文件就是程序的配置文件app.config记得生成到目录下,配置终结点。服务类生成的程序需要安装,还需要继承install实现一个安装类,把生成的服务程序安装到windows系统服务去,然后就可以在服务中启动服务,

这个安装是在管理员权限下进入对应的.netframework目录下运行installutil path(程序目录) 这里用的是4.5 ,就是v4.0。。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GettingStartedLib;
using System.ServiceModel.Description;
using System.ServiceModel;
using System.ServiceProcess;
using System.Configuration.Install;
using System.ComponentModel;

namespace WcfConsole
{
    public class serviceCalss : ServiceBase
    {
        public ServiceHost serviceHost = null;
        public serviceCalss()
        {
            ServiceName = "WCFWindowsServiceSample";

        }
        protected override void OnStart(string[] args)
        {
            if (serviceHost != null)
            {
                serviceHost.Close();
            }
            serviceHost = new ServiceHost(typeof(Calculator));

            serviceHost.Open();
        }

        protected override void OnStop()
        {
            if (serviceHost != null)
            {
                serviceHost.Close();
                serviceHost = null;
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {

            ServiceBase.Run(new serviceCalss());

            //服务的地址
            Uri baseAddress = new Uri("http://localhost:8000/GettingStarted/");
            //承载服务的宿主
            ServiceHost selfHost = new ServiceHost(typeof(Calculator), baseAddress);

            //try
            //{
            //    selfHost.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "Calculator");
            //    ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            //    smb.HttpGetEnabled = true;
            //    selfHost.Description.Behaviors.Add(smb);
            //    selfHost.Open();
            //    Console.WriteLine("The service is ready.");
            //    Console.WriteLine("input<exit> to terminate service.");
            //    Console.WriteLine();
            //    while ("exit" == Console.ReadLine())
            //    {
            //        selfHost.Close();
            //    }
            //}
            //catch (CommunicationException ex)
            //{
            //    Console.WriteLine(ex.Message);
            //    selfHost.Abort();
            //    Console.ReadLine();
            //}
        }
    }

    [RunInstaller(true)]
    public class ProjectInstaller : Installer
    {
        private ServiceProcessInstaller process;
        private ServiceInstaller service;

        public ProjectInstaller()
        {
            process = new ServiceProcessInstaller();
            process.Account = ServiceAccount.LocalSystem;
            service = new ServiceInstaller();
            service.ServiceName = "WCFWindowsServiceSample";
            Installers.Add(process);
            Installers.Add(service);
        }
    }
}

对应的配置文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
    </startup>
  <system.serviceModel>
    <services>
      <!-- This section is optional with the new configuration model
           introduced in .NET Framework 4. -->
      <service name="GettingStartedLib.Calculator"
               behaviorConfiguration="CalculatorServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000/"/>
          </baseAddresses>
        </host>
        <endpoint address=""
                  binding="wsHttpBinding"
                  contract="Calculator" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="CalculatorServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="False"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

4 服务端承载好了,就可以用客户端调用服务了,又两种方式

  客户端直接引用已经运行的服务,vs会自动生成一个对应的client类,里边又设置好的服务路径和接口之类的,也可以自己指定路径,只用他的函数,记得close

           // CalculatorClient client = new CalculatorClient();
            var client = myChannelFactory.CreateChannel();


            double value1 = 100.00D;
            double value2 = 15.99D;
            double result = client.Add(value1, value2);
            Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);

  也可以用ChannelFactory<TChannel>类来构造一个client,这种模式是引用服务的契约接口,

     var myBinding = new WSHttpBinding();
            EndpointAddress myEndpoint = new EndpointAddress("http://localhost:8000/GettingStarted/");
            //EndpointAddress myEndpoint = new EndpointAddress("http://localhost:8000/"); 
            ChannelFactory<ICalculator> myChannelFactory = new ChannelFactory<ICalculator>(myBinding, myEndpoint);

  这里边都可以用配置文件来设置终结点,也就是绑定类型和地址,配置方式与服务端类似

5,6就是配置和调用,这里就不说了

这里只是简单的wcf实例,

实际还有内容没有涉及:

1 不同的绑定类型,怎么选择,网上有个图是判断什么情况选择对应的绑定类型,其实就是选择协议

2 加密,因为之间的程序用的是basichttpbingding就是就是基本的http绑定,客户端没用引用服务端,服务端以web服务的形式公开服务,客户端直接以http请求的形式来与服务端通信,这样客户端cs和bs可以公用一个服务端,但是没有加密,传输的内容是完全公开的··讲道理web服务这玩意本来就是发过去给人看的·公开应该也没啥好像····,面试的时候问到这个问题直接懵逼了···这里暂时没写 ··这玩意好像挺麻烦的···

3 这里在4.5的环境下编写的,服务公开以后会自动生成对应的异步执行方法,与之前的方式不同,之前是老式的beginxxx的异步方式,返回一个Iasyncresult的结果,这里是4.5的async/await的形式来执行异步操作

4 wcf服务是可以兼容asp.net ,也可以返回json格式的对象··这个可以通过设置方法的操作特性可以设置

5 wcf服务支持双工,这个就很流弊了,不过讲道理tcp这种本身就支持双工,没具体研究

6 在双工模式下支持回调,这个意思好像是客户端请求完成再出发一个回调服务么··· 没研究···

。。。。。。

 

转载于:https://www.cnblogs.com/onegarden/p/7134426.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30267697/article/details/96901578

智能推荐

c++—0/1背包问题--贪心算法(详解)_背包问题贪心算法-程序员宅基地

文章浏览阅读1.3w次,点赞15次,收藏159次。贪心算法的基本思想•贪心算法的特点是每个阶段所作的选择都是局部最优的,它期望通过所作的局部最优选择产生出一个全局最优解。贪心与动态规划:与动态规划不同的是,贪心是鼠目寸光;动态规划是统揽全局。贪心:每个阶段产生的都是局部最优解贪心算法的基本要素•贪心选择性质:所求问题的全局最优解可以通过一系列局部最优的选择(即贪心选择)来达到。–这是贪心算法与动态规划算法的主要区别。•最优子结构性质:当原问题的最优解包含子问题的最优解时,称此问题具有最优子结构性质。最优子结构性质是该问题可用_背包问题贪心算法

手把手教你如何使用Fiddler工具,从入门到精通只要看完全文_fiddler使用教程-程序员宅基地

文章浏览阅读4.6k次,点赞3次,收藏14次。Fiddler 是一个 HTTP 协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的 HTTP 通讯。Fiddler 提供了电脑端、移动端的抓包、包括 http 协议和 https 协议都可以捕获到报文并进行分析;可以设置断点调试、截取报文进行请求替换和数据篡改,也可以进行请求构造,还可以设置网络丢包和延迟进行 APP 弱网测试等。Fiddler安装包他工具等学习资资料【点击这!!】02.安装 Fiddler访问 fiddler 下载网址,选择 Wi._fiddler使用教程

腾讯T3大牛亲自教你!美团二面惜败,源码+原理+手写框架_美团app手写程序-程序员宅基地

文章浏览阅读70次。引言技术好就一定能写出优质的代码吗?不能,对于做开发的人来说,学到的技术知识是理论性的,只有把知识代入到真实的实战案例中才能快速将理论转变为技能。一直在网上看到过很多关于类似的资料,要么就是版本比较老,要么就是讲的太干,没有阅读下去的兴趣,以至于在这方面的学习止步不前。今天,我跟大家要分享的这份阿里《互联网实战案例》共有22大实战案例,每一章节都有对应的完整代码,同时在学习的过程中需要了解并运行代码,再也不用担心学不会了。Java代码是怎么运行的?Java的基本类型Java虚拟机是如何加载J_美团app手写程序

Python 实现 2048 游戏_实验:2048游戏 实验目标 理解类和对象的思想 掌握python标准gui库—tki-程序员宅基地

文章浏览阅读4.5k次,点赞3次,收藏24次。Python 实现 2048 游戏一、实验介绍2048 是 20 岁的 Gabriele Cirulli 开发的一款数字游戏,曾风靡一时。这次实验我们用 200 行 Python 代码,在终端环境中实现一个 2048 规则的小游戏。二、实验知识点本节实验中将学习和实践以下知识点:Python 基本知识curses 终端图形编程库random 随机数模块collections 容器数据类型库状态机的概念三、适合人群本课程通过 Python 实现了一个在终端上运行的 2048 小游戏,_实验:2048游戏 实验目标 理解类和对象的思想 掌握python标准gui库—tki

HDU----1098_hdu. original id: 1098-程序员宅基地

文章浏览阅读66次。题目链接http://acm.hdu.edu.cn/showproblem.php?pid=1098#include<stdio.h>int main(void){ int n,i; int a; while(scanf("%d",&n)!=EOF) { for(a=0;a<=65;a++)//由于x取1,a的范围为0~65 { //找规律 (65为周期) if((18+_hdu. original id: 1098

获取APP包名和activity的几种方法_获取appactivity-程序员宅基地

文章浏览阅读4.1k次,点赞3次,收藏13次。一、只有Apk的情况(1)aapt 使用命令行aapt dump xmltree ColaBox.apk AndroidManifest.xml(2)使用apktool 使用反编译工具apktool,反aaaa编译后打开AndroidManifest.xml文件,查找方式同“有源码情况”(3)aapt二、有源码情况直接打开AndroidManifest.xml文件,找到包含android.intent.action.MAIN和android.intent.category.LAU_获取appactivity

随便推点

两款数学工具的介绍_zh.numberempire-程序员宅基地

文章浏览阅读7.4k次,点赞8次,收藏4次。两款数学工具的介绍文章目录两款数学工具的介绍一、简介二、数学帝国(数学工具)三、在线LaTex公式编辑器一、简介我们在这里介绍两款十分常用而且十分有用的数学工具。二、数学帝国(数学工具)https://zh.numberempire.com/三、在线LaTex公式编辑器https://www.latexlive.com/home##这两个工具都是使用方便快捷,无需教程而且是免费的,希望对大家有一些帮助了啦。..._zh.numberempire

js发起http请求_js http请求-程序员宅基地

文章浏览阅读4.6k次,点赞2次,收藏4次。JS如何发起http请求?_js http请求

Map有序转成有序json_map序列化json-程序员宅基地

文章浏览阅读559次。Map有序转成有序json项目里要对一些签名的请求值签名,后台来验签,本来的是使用一些特殊的方法直接序列化的,下面给出例子。后来使用自定义的签名方法,得要自己序列化,以便于和后台验签方法一致。之前的Map序列化 //代码片 List<Map.Entry<String, Object>> mappingList = null; mappingList = new ArrayList<Map.Entry<String, Object>>(signMap._map序列化json

MATLAB麻雀优化CNN超参数分类_为什么用优化算法优化cnn-程序员宅基地

文章浏览阅读6.5k次,点赞19次,收藏149次。在CNN分类器模型的构建中,涉及到特别多的超参数,比如:学习率、训练次数、batchsize、各个卷积层的卷积核大小与卷积核数量(feature map数),全连接层的节点数等。直接选择的话,很难选到一组满意的参数,因此可以用优化算法进行超参数优化,虽然过程比较慢,但是总比自己无脑试的效果好。 基于此思想,本文采用麻雀优化算法,对CNN上述9个超参数进行优化。1,麻雀优化算法原理 麻雀优化是2020年提出来得,具体原理:原理点这里2、麻雀优化CNN的..._为什么用优化算法优化cnn

如何彻底的删除MySQL数据库(注册表未清理,请查清理注册表的)_如何删除注册表里面的mysqlcsdn-程序员宅基地

文章浏览阅读973次。以下操作以Window7操作系统为例:1)停止window的MySQL服务。找到“控制面板”-> “管理工具”-> “服务”,停止MySQL后台服务。2)卸载MySQL安装程序。找到“控制面板”-> “程序和功能”,卸载MySQL程序。3)删除MySQL安装目录下的所有文件。(删除安装的文件夹)4)删除c盘ProgramDate隐藏目录中关于MySQL的目录。(删除文件存放的文件夹)4.1 打开window系统的“显示隐藏文件”功能,以便查看到系统的所有隐藏文件4.2 _如何删除注册表里面的mysqlcsdn

js正则表达式应用_"var str=\"hello,everyone.my name is gui, 18 years-程序员宅基地

文章浏览阅读572次。2012年3月19日 天气有雨 没事找事找 不懂得拒绝 招来的事 往心里面咽! //js中正则表达式学习 //var re=new RegExp();//RegExp是一个对象,但这样没什么效果,需要将正则表达式的内容作为字符串传进去 //re=new RegExp("a");//创建一个匹配a字符的表达式_"var str=\"hello,everyone.my name is gui, 18 years old.\"; var arr="