自强不息,厚德载物!身心自在,道法自然!


WCF之略窥门径

WCF是什么东东?

   WCF,是.NET Framework 3.0中的四个组件之一,是微软专门针对面向服务(Service Oriented)应用程序提供的一个分布式编程框架,可以使用托管代码建立和运行SOA的软件系统。它使得开发者能够建立一个跨平台的、安全、可信赖、事务性的解决方案,且能与已有系统兼容协作。WCF是微软分布式应用程序开发的集大成者,它整合了.Net平台下所有的和分布式系统有关的技术,例如.Net Remoting、ASMX、WSE和MSMQ,并且从分整合了.Net Remoting/Asp.Net/Xml/Web Service/MSMQ/WSE/Enterprise Service等多项分布式技术,取其精华,弃其糟粑 。以通信(Communiation)范围而论,它可以跨进程、跨机器、跨子网、企业网乃至于Internet;以宿主程序而论,可以以ASP.NET,EXE,WPF,Windows Forms,NT Service,COM+作为宿主(Host)。WCF可以支持的协议包括TCP,HTTP,跨进程以及自定义,安全模式则包括SAML,Kerberos,X509,用户/密码,自定义等多种标准与模式。全称"Windows Communication Foundation".

整个WCF的架构结构图下图所示(图片来源http://dev.yesky.com/402/8079902.shtml

         

WCF整个架构层次简单的说明:

      Contracts(契约):契约定义了整个消息系统的各个方面。而WCF契约有分好多种,数据契约描述了服务传递的每个消息的具体参数消息契约使用SOAP来定义消息的具体格式服务契约定义服务接口的方法签名而策略和绑定规定访问服务的通信条件。(这些具体的契约我们后续一起慢慢完善它,先了解了解哈!大牛Artech等博客上都有详细的描述哈)。

      Service Runtime(服务运行时):服务运行时包含了在对服务进行实际操作时才发生的一些行为,即是服务的运行时行为。WCF也分好几种,节流阀行为(Throttling Behavior),控制着有多少消息能被处理错误行为(Error Behavior)设定服务出现内部错误时,控制哪些信息被传递到客户端元数据行为(Metadata Behavior)控制着哪些元数据暴露给外部实例行为(Instance Behavior)控制着能运行多少服务的实例事务行为(Transaction Behavior)在出现错误时保证事务操作能回滚调度行为(Dispatch Behavior)控制着消息如何被整个WCF基础结构进行处理并发行为(Concurrency Behavior)控制在服务运行的并发处理;参数过滤器(Parameter Filtering)控制着参数的过滤条件。

     Messaging(消息):消息层实际上由一些通道(Channel)所组成。所谓通道,就是一个以特定方式处理消息的组件。一系列的通道串联起来就成为通道栈通道分为两种类型,协议通道和传输通道。协议通道有:WS安全协议通道(WS-Security Channel)、WS消息可靠性协议通道(WS-Reliability Channel)。传输通道有:HTTP通道(HTTP Channel)、TCP通道(TCP Channel)、命名管道通道(NamedPipe Channel)和消息队列通道(MSMQ Channel)。另外还有些编码通道(Encoders Channel)和事务流通道(Transaction Flow Channel)作为辅助,有兴趣的朋友可以更深的了解了解。

    Hosting and Activation(宿主和激活):服务必须在一个执行程序中运行。服务一般托管在外部可执行程序里面,如IIS和Windows激活服务(Windows Activation Service,WAS)。

以上是对WCF架构层次的简要说明,是不是感觉这个东西很强大啊!嘿嘿,那就写一个用WCF的小实例吧!

做一个订车票简单的简单小例子吧!首先来检查一下自己机器的环境,看是否满足开发WCF的需求(嘿嘿,瞎扯现在估计我们猿人的编程机器不想过去那么落后吧)!

开始,我们先做一个WCF的应用程序,如下图1。

图1.

当点击完成后,VS会为我们创建好如下图2的项目。

图2.

从图2可以一目了然的看到IService1.cs应该就是传说中的WCF的服务契约,当然上图是VS自动生成的一个结构。开始接才接触的人肯定奇怪怎么还有一个Service1.svc的文件,这个文件是干什么的么?

那就扯开话题来看看这个是怎么回事—>在archive博客上有这样的描述:在采用IIS/WAS进行服务寄宿的情况下,我们需要为寄宿的服务创建一个.svc文件。在通常的情况下(当然你也可以以内联的形式将整个服务类型也定义其中),我们仅仅在该.svc文件中定义基本的<%@ServiceHost%>指令信息。其中最重要的指令信息自然是通过Service属性指定的寄宿服务的类型(实际上调用ServiceHostFactory的CreateServieHost方法传入的第一个参数值)。进一步来说,如果服务端能够维护一个Service/ServiceHostFactory与请求地址之间的映射关系,我们就可以不再需要.svc文件,因为.svc对于服务激活来说就是起到了这么一个映射的作用。

嘿嘿,恍然大悟吧!这个Service1.svc在这里根据VS生成的代码是实现IService1.cs里的服务契约。转回话题,我们直接来继续写代码吧!

下面就是直接修改IService1.cs的服务契约:

View Code
namespace HuiTaiWcf
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        void Add_Ticket(int count);   //添加车票的方法

        [OperationContract]
        int Buy_Tickets(int Num);     //购买车票的方法

        [OperationContract]
        int GetdemandNum();           //查询车票的方法 

        // TODO: Add your service operations here
    }
    // Use a data contract as illustrated in the sample below to add composite types to service operations.
    [DataContract]
    public class Ticket 
    {
        bool T_Count = true;        //判断是否还有车票
        int T_HowMany = 50;         //默认的车票

        [DataMember]
        public bool BoolTicket 
        {
            get { return T_Count; }
            set
            {
                if (HowMany > 0)
                {
                    T_Count = false;
                }
                else
                {
                    T_Count = true;
                }
            }
        }
        [DataMember]
        public int HowMany 
        {
            get { return T_HowMany; }
            set { T_HowMany = value; }
        }
    }
}
代码说明:[ServiceContract] 这个特性告诉编译器,该类型(指IInterface1)是一个服务契约。 [OperationContract] 这个特性告诉编译器,该成员(指Function1)是一个操作契约。有此可以看出来一个服务契约是
由一个或者多个操作契约组成的。Ticket类是一个是自己定义的类目的是用于在服务端和客户端经行数据传输;[DataContract]数据契约则是服务端和客户端之间要传送的自定义数据类型,一旦声明一个类型为DataContract,那
么该类型就可以被序列化在服务端和客户端之间传送,只有声明为DataContract的类型的对象可以被传送,且只有成员属性会被传递,成员方法不会被传递。[DataMember]特性就是我们数据契约里的数据成员,客户端(宿主)程序只
会获取应用带有DataMember的数据。粗略的描述下,如有什么错误,还请大家指点。
接下来需要在Service1.svc这个文件实现我们服务契约了:
View Code
namespace HuiTaiWcf
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
    public class Service1 : IService1
    {
        Ticket ticket = new Ticket();    //实例化一下我们自定义的Ticket类型
        //实现一下契约的添加方法
        public void Add_Ticket(int count) 
        {
            this.ticket.HowMany = this.ticket.HowMany + count;
        }
        //实现一下契约的购买方法
        public int Buy_Tickets(int Num)
        {
            if (this.ticket.BoolTicket)
            {
                this.ticket.HowMany = this.ticket.HowMany - Num;
                return 1;
            }
            else 
            {
                return 0;
            }
        }
        //实现一下契约的查询方法
        public int GetdemandNum() 
        {
            return this.ticket.HowMany;
        }
    }
}

到这里,我们的服务端就暂时说一下( ^_^ )/~~拜拜!搞好服务,是不是要一个漂亮的东东来启动一下我们费劲写的服务呢!
那就加winForm窗体进来玩玩吧!添加项目如下图3.

图3.

早上换了一个环境,但是不影响我们的步伐,继续写。

先画一个WinFrom窗体程序,大体模样如下图4.

图4.为了好看加了一个背景图。窗体画好,直接写后台吧!

哦,这里要注意一点,因为我们用WinFrom来写WCF服务端的一个开启服务的宿主程序,所以我们要在项目里应用System.ServiceModel这个DLL,为什么呢!因为WCF中的大部分类和接口都在这个DLL里面呢!

来看WinFrom后台代码,这里无非也就是2个按钮的事件罢了。

View Code
namespace ServiceForms
{
    public partial class ServiceForm : Form
    {
        public ServiceForm()
        {
            InitializeComponent();
        }
        //定义ServiceHost
        ServiceHost host = null;
        //启动服务
        private void butStart_Click(object sender, EventArgs e)
        {
            //HuiTaiWcf.Service1需要引用服务端的DLL
            host = new ServiceHost(typeof(HuiTaiWcf.Service1));
            host.Open();
            this.label.Text = "服务已经启动";
        }
        //关闭服务
        private void butstop_Click(object sender, EventArgs e)
        {
            //判断服务是否关闭
            if(host.State != CommunicationState.Closed)
            {
                host.Close();
            }
            this.label.Text = "服务已经关闭";
        }
    }
}

这里相信大家一看也是一目了然的代码了,要说的无非也就是这个"ServiceHost",那就简单的说下"ServiceHost",一旦创建好ServiceHost对象,通过它就可以创建WCF运行时(Runtime),WCF 运行时是自一组负责接受和发送消息的对象。这个就简单的了解下,后续我们在共同学习。
接下来为我们启动服务的的WinFrom程序来创建一个App.config文件,来配置一下:

View Code
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <!--添加服务-->
      <service name="HuiTaiWcf.Service1" behaviorConfiguration="CalculatorServiceBehavior">
        <!--name必须与代码中Host实例初始化的服务一样-->
        <host>
          <baseAddresses>
            <!--添加调用服务的地址-->
            <add baseAddress="http://localhost:8000"/>
          </baseAddresses>
        </host>
        <!--添加契约接口contract="WcfDemo.IService1"WcfDemo.IService1为契约接口-->
        <endpoint address ="" binding="wsHttpBinding" contract="HuiTaiWcf.IService1" ></endpoint>
      </service>
    </services>
    <!--定义CalculatorServiceBehavior的行为-->
    <behaviors>
      <serviceBehaviors>
        <behavior name="CalculatorServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

这个配置算对运用WCF的契约那可是很重要的,这就是WCF中出名的ABC.有些人可能还不知道ABC是什么东东吧!那就撤下这个所谓的WCF中出名的ABC吧!
说ABC之前,先来说下Endpoints,它是WCF实现通信的核心要素,它可以是一个,也可以是一族;WCF Service由一个Endpoints集合组成,每个Endpoint就是用于通信的入口,客户端和服务端通过Endpoint交换信息。如下图5.

图5.

从图中就可以看出Endpoint是由三部分组成:Address,Binding,Contract.这个就是WCF中出名的ABC,有了他们客户端和服务端通过就能很好的实现通讯。那他们具体都是什么呢!那就在简单的说下。Address是Endpoint的网络地址,它标记了消息的发送的目的地。Binding描述是如何发送消息。Contract则描述的是消息所包含的内容,以及消息的组织和操作方式。可以这么理解:当WCF发送消息时,通过Address知道消息的发送的地址,通过Binding知道怎样来发送它,通过contract知道发送的消息是什么。

转会话题,配置弄好了,来看看我们的WinFrom是不是能启动服务端呢!

我们需要在我们的WinFrom程序右键,点击选择"调试"里面的"启动新的实例",如图6.

 

图6.

启动好以后,我们的Winfrom窗体就出来,我们点击"开启服务"看看,如下图7.

图7.到这里说明我已经启动了WCF的服务,为了更验证,我们只需要在浏览器里输入我们刚才在App.config里配置的添加调用的服务地址,我配置的是:http://localhost:8000/,我们来试着访问下,访问结果如下图8所以。

 

图8.到这里已经说明我们的服务端和启动的服务已经是ok的了,我们启动WCF的服务,无非就想要暴露里面的契约,使其他程序使用或者实现,那么我们是不是还差一个客户端程序,那就在写一个简单的客户端吧!客户端用控制台不太友好,那就干脆在来一个WinFrom程序吧!我们在我们的项目就在添加一个WinFrom程序吧!

在新建我们的客户端之前我在这里说一个小提示:所有系统是Win7的朋友们,当你在运行那个WInFrom开关程序时,可能会出现一个"HTTP无法注册 UrL Http://+8000/. 进程不具有该命名空间的权限"的异常,如下图9.

图9.解决这个异常的办法就是你在启动Visual Studio时,右键选择"以管理员身份运行"即可。

转回来搞我们的客户端程序吧!

添加我们的客户端WinFrom程序吧!如下图10.

图10.添加完之后就画个窗体吧!如图11.

图11.然后我们就要写后台程序了,现在我们是万事俱备只欠东风了。在写后台之前我们需要建立WCF引用了。

具体的操作如图12.我们在我们客户端WinFrom程序里的引用上右键,"添加服务引用",当我们点击添加服务引用后,会出现如图12.的窗体

图12.当出现这个窗口时,开始里面是空白的什么都没有,这个时候你可以选择"发现"按钮,VS则会自动在你的解决方案里检查暴露的服务。当然你明确服务的地址的话,可以在地址栏里面输入服务的URL。当我们找到服务后,就把它引用到我们的客户端的winFrom程序吧!

引入到项目,行了里会多了如下图13的东西。

图13.有兴趣的朋友可以去看看了解了解,Artech等人博客上都有这方面的知识。

现在就把我们客户端的后台给随便搞了呗!

View Code
namespace WindowsWCF
{
    public partial class WindowWCF : Form
    {
        public WindowWCF()
        {
            InitializeComponent();
        }
        //声明在客户端调用服务
        ServiceReference1.Service1Client T_Client = new ServiceReference1.Service1Client();
        //购买
        private void butGet_Click(object sender, EventArgs e)
        {
            int o = this.T_Client.Buy_Tickets(2);    //调用WCF中的方法
            if (o==1)
            {
                this.label1.Text = "购买成功";
            }
            this.label1.Text = "还剩下车票" + this.T_Client.GetdemandNum().ToString() + "";
        }
        //查询
        private void butmian_Click(object sender, EventArgs e)
        {
            this.label1.Text = "";
            this.label1.Text = this.T_Client.GetdemandNum().ToString();
        }
    }
}

现在客户端的程序也完成了代码里注释很明确,那跑起来看看,看我们的WCF的服务到底客户端怎么样呢!如图14所示的结果.

图14.

呵呵!一个简简单单的例子希望对新学西WCF的朋友有点帮助,我也是才接触这东西,那里有什么错误的地方,还请大家多多指导,大家一起共同学习!

posted @ 2012-05-08 11:14  辉太  阅读(1593)  评论(3编辑  收藏  举报

路漫漫其修远兮,吾将上下而求索!