代码改变世界

Chapter 1.5:WCF实践 托管

2011-01-11 19:32  田志良  阅读(...)  评论(... 编辑 收藏

1. 源码下载


2. 托管简介

  WCF服务类不能凭空存在。每个 WCF 服务都必须托管(Hosting) 在 Windows 进程中,该进程被称为宿主进程(Host Process)。单个宿主进程可以托管多个服务,而相同的服务类型也能够托管在多个宿主进程中。WCF 没有要求宿主进程是否同时又是客户端进程。显然,一个独立的进程有利于错误与安全的隔离。谁提供进程或是提供何种类型的进 程并不重要。宿主可以由 IIS 提 供,也 可以由 Windows Vista 的 Windows 激活服务(Windows Activation Service,WAS)提供,或者开发者直接将它作为应用程序的一部分。

 

3. WCF服务和客户端在同一进程中

  当WCF宿主和客户端都在同一个应用程序域时,采用命名管道通信。命名管道在同一台计算机的不同进程之间,或在跨越一个网络的不同计算机的不同进程之间,支持可靠的,单向或双向的数据通信。虽然Windows的命名管道机制允许跨主机的交互,但是 WCF 对 netNamedPipeBinding 做了特殊的限制,使得通信的双方只能部署在同一台主机上。

  契约类如下:

using System;
using System.ServiceModel;

namespace WCF.Chapter1.InProc
{
    [ServiceContract]
    public interface Contract
    {
        [OperationContract]
        string say();
    }
}

  服务类如下:

using System;
using System.ServiceModel;

namespace WCF.Chapter1.InProc
{
    public class Service : Contract
    {
        public string say()
        {
            return "我不是一个随便的人,随便起来不是人!";
        }
    }
}

  客户端代理类如下:

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace WCF.Chapter1.InProc
{
    public class ClientProxy : ClientBase<Contract>, Contract
    {
        public ClientProxy()
        { }

        public ClientProxy(string configurationName) :
            base(configurationName)
        { }

        public string say()
        {
            return base.Channel.say();
        }

    }
}

  配置文件如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="WCF.Chapter1.InProc.Service">
        <endpoint address="net.pipe://localhost/InProc" binding="netNamedPipeBinding" contract="WCF.Chapter1.InProc.Contract"></endpoint>
      </service>
    </services>
    <client>
      <endpoint address="net.pipe://localhost/InProc" binding="netNamedPipeBinding" contract="WCF.Chapter1.InProc.Contract"></endpoint>
    </client>
  </system.serviceModel>
</configuration>

  宿主类如下:

using System;
using System.ServiceModel;

namespace WCF.Chapter1.InProc
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(Service)))
            {
                host.Opened += delegate
                {
                    Console.WriteLine("服务已开启...");
                };
                host.Open();

                using (ClientProxy proxy = new ClientProxy())
                {
                    Console.WriteLine("我是一个什么样的人?");
                    Console.WriteLine(proxy.say());
                }

                Console.ReadLine();
            }

            Console.WriteLine("服务已关闭...");
            Console.ReadLine();
        }
    }
}

 

4. WCF服务和客户端在同一进程不同应用程序域中

  和上例一样,应用程序域之间通信,采用命名管道通信。应用程序域 (AppDomain) 可以被看作一个轻型的进程。在一个 Win32 进程中可以存在多个 AppDomain。应用程序域使应用程序以及应用程序的数据彼此分离,有助于提高安全性。

  下面着重讲解一下 AppDomainHost 通用类:

using System;
using System.ServiceModel;

namespace WCF.Chapter1.AppDomainHosting
{
    public class ServiceHostActivator : MarshalByRefObject
    {
        ServiceHost m_Host;

        public void SetType(Type serviceType)
        {
            Uri baseAddress = new Uri("net.pipe://localhost/");
            m_Host = new ServiceHost(serviceType, baseAddress);
        }

        public void Open()
        {
            m_Host.Opened += delegate
            {
                Console.WriteLine("服务已开启...");
            };
            m_Host.Open();
        }

        public void Close()
        {
            m_Host.Close();
        }

        public void Abort()
        {
            m_Host.Abort();
        }
    }
}
using System;
using System.ServiceModel;
using System.Diagnostics;
using System.Reflection;

namespace WCF.Chapter1.AppDomainHosting
{
    public class AppDomainHost<T> : IDisposable
    {
        ServiceHostActivator m_ServiceHostActivator;

        public AppDomainHost() :
            this("AppDomain Host For " + typeof(T) + " " + Guid.NewGuid())
        { }

        public AppDomainHost(string appDomainName)
        {
            Debug.Assert(AppDomain.CurrentDomain.FriendlyName != appDomainName);
            AppDomain newDomain = AppDomain.CreateDomain(appDomainName);
            string assemblyName = Assembly.GetAssembly(typeof(ServiceHostActivator)).FullName;
            m_ServiceHostActivator = newDomain.CreateInstanceAndUnwrap(assemblyName, typeof(ServiceHostActivator).ToString()) as ServiceHostActivator;
            m_ServiceHostActivator.SetType(typeof(T));
        }

        public void Open()
        {
            m_ServiceHostActivator.Open();
        }

        public void Close()
        {
            m_ServiceHostActivator.Close();
        }

        public void Abort()
        {
            m_ServiceHostActivator.Abort();
        }

        void IDisposable.Dispose()
        {
            Close();
        }

    }
}

  首先,在构造函数中,获取当前应用程序域名,如果与要创建的应用程序域同名,则阻塞并警报。然后,创建应用程序域,实例化宿主,并将宿主实例加载到应用程序域中运行。ServiceHostActivator 类提供了对服务的开启、关闭、强行退出功能,在 AppDomainHost 中实现了对 ServiceHostActivator  类的单例引用, 可以用 AppDomainHost 实例管理服务状态。

 

5. WCF服务包含多个终结点

  一个应用程序包含多个应用程序域,一个应用程序域包含多个WCF宿主实例,一个WCF宿主实例对应一个服务类型,一个服务类型对应多个终结点。

  本例主要介绍在一个WCF宿主实例中实现多个终结点通信。

  配置文件如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="WCF.Chapter1.MutipleEndpoints.Host.Service" behaviorConfiguration="MEXConfig">
        <endpoint address="net.tcp://localhost:8001" binding="netTcpBinding" contract="WCF.Chapter1.MutipleEndpoints.Host.Contract"></endpoint>
        <endpoint address="net.tcp://localhost:8001/Say" binding="netTcpBinding" contract="WCF.Chapter1.MutipleEndpoints.Host.Contract"></endpoint>
        <endpoint address="net.tcp://localhost:8002/Say" binding="netTcpBinding" contract="WCF.Chapter1.MutipleEndpoints.Host.Contract"></endpoint>
        <endpoint address="net.tcp://localhost:8003/Say" binding="netTcpBinding" contract="WCF.Chapter1.MutipleEndpoints.Host.Contract"></endpoint>
        <endpoint address="http://localhost:8004/Say" binding="basicHttpBinding" contract="WCF.Chapter1.MutipleEndpoints.Host.Contract"></endpoint>
        <endpoint address="MEX" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
        <endpoint address="MEX" binding="mexTcpBinding" contract="IMetadataExchange"></endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MEXConfig">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

  客户端代理调用如下:

using System;

namespace WCF.Chapter1.MutipleEndpoints.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            ClientProxy proxy1 = new ClientProxy("say1");
            Console.WriteLine("net.tcp://localhost:8001: " + proxy1.say());

            ClientProxy proxy2 = new ClientProxy("say2");
            Console.WriteLine("net.tcp://localhost:8001/Say: " + proxy2.say());

            ClientProxy proxy3 = new ClientProxy("say3");
            Console.WriteLine("net.tcp://localhost:8002/Say: " + proxy3.say());

            ClientProxy proxy4 = new ClientProxy("say4");
            Console.WriteLine("net.tcp://localhost:8003/Say: " + proxy4.say());

            ClientProxy proxy5 = new ClientProxy("say5");
            Console.WriteLine("http://localhost:8004/Say: " + proxy5.say());

            proxy1.Close();
            proxy2.Close();
            proxy3.Close();
            proxy4.Close();
            proxy5.Close();

            Console.ReadLine();
        }
    }
}

 

6. IIS寄宿

  在微软的 Internet 信息服务器(Internet Information Server,IIS)中托管服务,主要的优势是宿主进程可以在客户端提交第一次请求的时候自动启动,还可以借助 IIS 管理宿主进程的生命周期。IIS 托管的主要缺点在于只能使用 HTTP 协议。如果是 IIS 5,还要受端口限制,要求所有服务必须使用相同的端口号。

  在 IIS 中托管服务,需要在 IIS 下创建虚拟目录,并提供一个 .svc 文件:

<%@ ServiceHost Language="C#" Debug="true" Service="WCF.Chapter1.SVC.Host.Service" %>

  注:Service 为服务类的type名。

  配置文件如下:

<system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SVCBehavior">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
        <service name="WCF.Chapter1.SVC.Host.Service" behaviorConfiguration="SVCBehavior">
            <endpoint address="" binding="basicHttpBinding" contract="WCF.Chapter1.SVC.Host.IContract"/>
        </service>
    </services>
</system.serviceModel>

  .svc所在的地址是服务的Base Address,终结点可以通过Address指定一个相对地址,所以IIS寄宿服务无需通过代码创建宿主实例,在添加的终结点时也无须指定地址。