WCF开发之宿主(Hosting)

WCF想要对外提供服务,那么需要一个宿主来容纳这些服务。

宿主环境
• Self-hosting
– 控制台应用程序,Windows应用程序,Windows服务
– HTTP, TCP, named pipes, Microsoft® Message Queuing (MSMQ)
• IIS/Microsoft® ASP.NET
– HTTP
• Windows Activation Service (windows2008/IIS7的新东西)
– HTTP, TCP, named pipes, MSMQ

 

下面分别介绍这几种不同的宿主:

Self-Hosting
• ServiceHost实例必须进行初始化来为服务暴露出端点(endpoint)
• 每个ServiceHost与指定的服务类型(接口)相关联
• Self-hosting环境手动创建实例
• 核心方法:
– Open() – 打开信道监听器
– Close() – 关闭信道监听器

ServiceHost配置(1)
• 可以通过程序进行配置:
ServiceHost host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService));
host.AddServiceEndpoint(typeof(HelloIndigo.IHelloIndigoService), new NetTcpBinding(),"net.tcp://localhost:9000/HelloIndigo");
host.Open();

ServiceHost配置(2)
以通过置设置进行初始化
ServiceHost host = new ServiceHost(typeof(HelloIndigo.HelloIndigoService));
host.Open();

• 可配:
<system.serviceModel>
<services>
<service name="HelloIndigo.HelloIndigoService" >
<endpoint address="net.tcp://localhost:9000/HelloIndigoService" binding="netTcpBinding" contract="HelloIndigo.IHelloIndigoService" />
</service>
</services>
</system.serviceModel>

这2个Demo就不给大家作了,在前面的文章中作了无数遍,呵呵。节约时间,继续往下看。

多服务(1)
ServiceHost hostA = null;
ServiceHost hostB = null;
try
{
hostA = new ServiceHost(typeof(BusinessServices.ServiceA));
hostB = new ServiceHost(typeof(BusinessServices.ServiceB));
hostA.Open();
hostB.Open();
Console.ReadLine();
}
finally
{
hostA.Close();
hostB.Close();
}

多服务(2)
<services>
<service name="BusinessServices.ServiceA">
<endpoint address="http://localhost:8000/ServiceA" contract="BusinessServices.IServiceA" binding="basicHttpBinding" />
</service>
<service name="BusinessServices.ServiceB">
<endpoint address="http://localhost:8000/ServiceB" contract="BusinessServices.IServiceB" binding="basicHttpBinding" />
</service>
</services>

ServiceHost事件
• 可以钩住ServiceHost事件:
– Opening, Opened
– Closing, Closed
– Faulted, UnknownMessageReceived
ServiceHost host = new ServiceHost(typeof(ExceptionService.Service));
host.Faulted += new EventHandler(OnFaulted);
host.Open();
static void OnFaulted(object sender, EventArgs e)
{
// TODO: report to administrator
}

服务行为(Service Behaviors)
• 可以通过程序与服务行为进行交互, 也可以通过配置的方式
ServiceHost host = new ServiceHost(typeof(ExceptionService.Service));
ServiceDebugBehavior debugBehavior = host.Description.Behaviors.Find<ServiceDebugBehavior>();
if (debugBehavior == null)
{
debugBehavior = new ServiceDebugBehavior();
host.Description.Behaviors.Add(debugBehavior);
}
debugBehavior.IncludeExceptionDetailInFaults = true;
host.Open();

 

IIS/WAS宿主
请求根据.svc文件在IIS中的扩展映射到中WCFService中
<% @ServiceHost="HelloIndigo.HelloIndigoService" %>
• 服务类型与@ServiceHost的声明相关
• ServiceHost实例化为服务类型

WCF开发之宿主(Hosting)_第1张图片

Self-Hosting 与 IIS/WAS
• Self-hosted的端点(endpoint)可以直接进行配置
• IIS/WAS 端点与.svc文件相关联

WCF开发之宿主(Hosting)_第2张图片

对于传统的IIS来说,如果WCF用它来做宿主,那么只支持Http的binding。

WCF开发之宿主(Hosting)_第3张图片

对于传统的IIS作为宿主有一个好处,就是当客户端发起一个请求,每个不同的请求会在同一服务进程的不同Domain里处理,也就是说如果一个恶意的攻击成功了,他只会影响到某一个App Domain,其他的Domain不会受到影响仍然可以正常工作,服务本身的进程也不会受到影响、那些运行的dll也不会受到影响,这是IIS和.Net Framework的运行方式决定的,是特性。如果用命令行窗口程序的话,一旦恶意攻击成功,那么整个服务就用可能完全瘫痪。

WAS(Windows Process Activation Service)他扩展出了不同Binding的监听器和接口,所以它可以适应更多的通信方式。IIS7中才支持。

WCF开发之宿主(Hosting)_第4张图片

Demo:

对于WAS目前只支持在IIS7中支持,并且操作系统Server2008。可以在Server Manager添加IIS7这个Feature,然后把WAS也同时安装进去,并且启动WAS服务Windows Process Activation Service。同时可以检查下Services里的相关监听器服务是否已启用(e.g. Net.Tcp.ListenerAdapter服务)。这些都没有问题了就可以使用WAS了。个人建议把所有WebServer的Feature都装上。

VS2008可以创建一个Web方式的WCF工程,很方便,变成方式一样,配置方式也差不多。应为时IIS管理服务,如果在IE中看不到服务的话,可以尝试选中目标虚拟目录,双击Directory Browsing,然后启用即可,一般都可以搞定。

WCF开发之宿主(Hosting)_第5张图片

唯一不太一样的地方是Service.svc: <%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %> 其他都差不多,配置是防盗Web.config里的。

 

< %@ ServiceHost  Language ="C#"  Debug ="true"  Service ="Service"  CodeBehind ="~/App_Code/Service.cs"  % >

 

Web.config代码
<? xml version="1.0" encoding="UTF-8" ?>
<!--
    Note: As an alternative to hand editing this file you can use the 
    web admin tool to configure settings for your application. Use
    the Website->Asp.Net Configuration option in Visual Studio.
    A full list of settings and comments can be found in 
    machine.config.comments usually located in 
    \Windows\Microsoft.Net\Framework\v2.x\Config 
-->
< configuration >


    
< configSections >
      
< sectionGroup  name ="system.web.extensions"  type ="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" >
        
< sectionGroup  name ="scripting"  type ="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" >
          
< section  name ="scriptResourceHandler"  type ="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"  requirePermission ="false"  allowDefinition ="MachineToApplication"   />
          
< sectionGroup  name ="webServices"  type ="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" >
            
< section  name ="jsonSerialization"  type ="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"  requirePermission ="false"  allowDefinition ="Everywhere"   />
            
< section  name ="profileService"  type ="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"  requirePermission ="false"  allowDefinition ="MachineToApplication"   />
            
< section  name ="authenticationService"  type ="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"  requirePermission ="false"  allowDefinition ="MachineToApplication"   />
            
< section  name ="roleService"  type ="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"  requirePermission ="false"  allowDefinition ="MachineToApplication"   />
          
</ sectionGroup >
        
</ sectionGroup >
      
</ sectionGroup >
    
</ configSections >   


    
< appSettings  />
    
< connectionStrings  />

    
< system.web >
        
<!--
            Set compilation debug="true" to insert debugging 
            symbols into the compiled page. Because this 
            affects performance, set this value to true only 
            during development.
        
-->
        
< compilation  debug ="false" >

          
< assemblies >
            
< add  assembly ="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"   />
            
< add  assembly ="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"   />
            
< add  assembly ="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"   />
            
< add  assembly ="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"   />
          
</ assemblies >

        
</ compilation >
        
<!--
            The <authentication> section enables configuration 
            of the security authentication mode used by 
            ASP.NET to identify an incoming user. 
        
-->
        
< authentication  mode ="Windows"   />
        
<!--
            The <customErrors> section enables configuration 
            of what to do if/when an unhandled error occurs 
            during the execution of a request. Specifically, 
            it enables developers to configure html error pages 
            to be displayed in place of a error stack trace.

        <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
            <error statusCode="403" redirect="NoAccess.htm" />
            <error statusCode="404" redirect="FileNotFound.htm" />
        </customErrors>
        
-->


      
< pages >
        
< controls >
          
< add  tagPrefix ="asp"  namespace ="System.Web.UI"  assembly ="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"   />
        
</ controls >
      
</ pages >

      
< httpHandlers >
        
< remove  verb ="*"  path ="*.asmx"   />
        
< add  verb ="*"  path ="*.asmx"  validate ="false"  type ="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"   />
        
< add  verb ="*"  path ="*_AppService.axd"  validate ="false"  type ="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"   />
        
< add  verb ="GET,HEAD"  path ="ScriptResource.axd"  type ="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"  validate ="false"   />
      
</ httpHandlers >
      
< httpModules >
        
< add  name ="ScriptModule"  type ="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"   />
      
</ httpModules >


    
</ system.web >

    
< system.codedom >
      
< compilers >
        
< compiler  language ="c#;cs;csharp"  extension =".cs"  warningLevel ="4"  type ="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
          
< providerOption  name ="CompilerVersion"  value ="v3.5"   />
          
< providerOption  name ="WarnAsError"  value ="false"   />
        
</ compiler >
        
< compiler  language ="vb;vbs;visualbasic;vbscript"  extension =".vb"  warningLevel ="4"  type ="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
          
< providerOption  name ="CompilerVersion"  value ="v3.5"   />
          
< providerOption  name ="OptionInfer"  value ="true"   />
          
< providerOption  name ="WarnAsError"  value ="false"   />
        
</ compiler >
      
</ compilers >
    
</ system.codedom >

    
< system.web.extensions >
      
< scripting >
        
< webServices >
          
<!--
              Uncomment this section to enable the authentication service. Include 
              requireSSL="true" if appropriate. 

          <authenticationService enabled="true" requireSSL = "true|false"/>
          
-->
          
<!--
              Uncomment these lines to enable the profile service, and to choose the 
              profile properties that can be retrieved and modified in ASP.NET AJAX 
              applications.

           <profileService enabled="true"
                           readAccessProperties="propertyname1,propertyname2"
                           writeAccessProperties="propertyname1,propertyname2" />
          
-->
          
<!--
              Uncomment this section to enable the role service.

          <roleService enabled="true"/>
          
-->
        
</ webServices >
        
<!--
        <scriptResourceHandler enableCompression="true" enableCaching="true" />
        
-->
      
</ scripting >
    
</ system.web.extensions >
    
<!--
        The system.webServer section is required for running ASP.NET AJAX under Internet
        Information Services 7.0.  It is not necessary for previous version of IIS.
    
-->
    
< system.webServer >
      
< validation  validateIntegratedModeConfiguration ="false"   />
      
< modules >
        
< add  name ="ScriptModule"  preCondition ="integratedMode"  type ="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"   />
      
</ modules >
      
< handlers >
        
< remove  name ="WebServiceHandlerFactory-Integrated"   />
        
< add  name ="ScriptHandlerFactory"  verb ="*"  path ="*.asmx"  preCondition ="integratedMode"  type ="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"   />
        
< add  name ="ScriptHandlerFactoryAppServices"  verb ="*"  path ="*_AppService.axd"  preCondition ="integratedMode"  type ="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"   />
        
< add  name ="ScriptResource"  preCondition ="integratedMode"  verb ="GET,HEAD"  path ="ScriptResource.axd"  type ="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"   />
      
</ handlers >
        
< directoryBrowse  enabled ="true"   />
    
</ system.webServer >


  
< system.serviceModel >
    
< services >
      
< service  name ="Service"  behaviorConfiguration ="ServiceBehavior" >
        
<!--  Service Endpoints  -->
        
< endpoint  address ="wsHttp"  binding ="wsHttpBinding"  contract ="IService"  name ="wsHttpBinding_IService" >
          
<!--  
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          
-->
          
< identity >
            
< dns  value ="localhost"   />
          
</ identity >
        
</ endpoint >

        
< endpoint  address ="netTcp"  contract ="IService"  binding ="netTcpBinding"  name ="netTcpBinding_IService"   />
        
< endpoint  address ="netPipe"  contract ="IService"  binding ="netNamedPipeBinding"  name ="netNamedPipeBinding_IService"   />
        
< endpoint  address ="basicHttp"  contract ="IService"  binding ="basicHttpBinding"  name ="basicHttpBinding_IService"   />
        
        
        
< endpoint  address ="mex"  binding ="mexHttpBinding"  contract ="IMetadataExchange"   />
      
</ service >
    
</ services >
    
< behaviors >
      
< serviceBehaviors >
        
< behavior  name ="ServiceBehavior" >
          
<!--  To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment  -->
          
< serviceMetadata  httpGetEnabled ="true"   />
          
<!--  To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information  -->
          
< serviceDebug  includeExceptionDetailInFaults ="true"   />
        
</ behavior >
      
</ serviceBehaviors >
    
</ behaviors >
  
</ system.serviceModel >
</ configuration >

 

Service.cs 代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Runtime.Serialization;
using  System.ServiceModel;
using  System.Text;

//  NOTE: If you change the interface name "IService" here, you must also update the reference to "IService" in Web.config.
[ServiceContract]
public   interface  IService
{
    [OperationContract]
    
string  GetData( int  value);
}

public   class  Service : IService
{
    
public   string  GetData( int  value)
    {
        
return   string .Format( " You entered: {0} " , value);
    }
}

 

 

Client 代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;

namespace  ConsoleApplication1
{
    
class  Program
    {
        
static   void  Main( string [] args)
        {
            ServiceReference.ServiceClient proxy 
=   new  ConsoleApplication1.ServiceReference.ServiceClient( " wsHttpBinding_IService " );
            
string  s  =  proxy.GetData( 1 );
            Console.WriteLine(s);

            proxy 
=   new  ConsoleApplication1.ServiceReference.ServiceClient( " netTcpBinding_IService " );
            s 
=  proxy.GetData( 2 );
            Console.WriteLine(s);

            proxy 
=   new  ConsoleApplication1.ServiceReference.ServiceClient( " netNamedPipeBinding_IService " );
            s 
=  proxy.GetData( 3 );
            Console.WriteLine(s);

            proxy 
=   new  ConsoleApplication1.ServiceReference.ServiceClient( " basicHttpBinding_IService " );
            s 
=  proxy.GetData( 4 );
            Console.WriteLine(s);
        }
    }
}

 

可以看出客户端是通过名字来区别使用哪个通信协议的。

在这里还要提一下WAS的配置方法,本人搞了半天才研究明白。

WCF开发之宿主(Hosting)_第6张图片

这些是默认站点的Binding配置,使自动建立的,我们需要知道Type的名称。然后,把这些Type到家到服务的虚拟目录的Advanced-Settings里,如下图:

WCF开发之宿主(Hosting)_第7张图片

只用作了上面的配置,WCF的服务才可以利用WAS来正常被客户端引用,否则会报出异常。

 

Windows应用程序: 通常用于在客户端安装,来控制WCF服务的开启和关闭。

Windows应用程序(1)
• Windows® Forms 或者WPF
• 从客户端主机暴露服务
• 需要对上下文同步有所认识
– UI线程或者其他线程
• 值得注意:
– ServiceHost需要手动打开
–判断服务是否需要上下文同步

Windows应用程序(2)
• 如果ServiceHost在非UI线程上打开,服务操
作会在新线程上进行操作
• 如果在UI线程上调用,服务会自动加入到该线
程上,除非UseSynchronizationContext
设置为false
– 可配置的服务行为(service behavior)

WCF开发之宿主(Hosting)_第8张图片

有些东西要知道:

[ServiceBehavior(UseSynchronizationContext=true, ConcurrencyMode=ConcurrencyMode.Reentrant)]主要区别在于对线程的管理。

ConcurrencyMode.Single:对于一个服务对象的调用,同时只允许一个现成在处理。不可重入的调用模型。
ConcurrencyMode.Reentrant:可重入的调用模型,同时只允许一个现成在处理,当一个调用请求调用了服务的一个方法,在服务方法中有调用了自己本身。
ConcurrencyMode.Multiple:完全并发访问,同时只允许多个现成在处理,可重入,但要代码控制线程安全。

Callback的过程:

Client--Service Request-->Service (1)

Client<--Callback Request--Service (2)

Client--Callback Response-->Service (3)

Client<--Service Response--Service (4) 

总体看来,Window应用程序作为宿主,要考虑很多线程方便的东西,尤其是在callback的方式下,其他的宿主可以自动控制,所以相比之下Windows应用程序在这方便相对麻烦一些。

Demo:

MessagingService 代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.ServiceModel;
using  System.Threading;
using  System.Windows.Forms;

namespace  Messaging
{
    [ServiceContract(Namespace 
=   " http://www.cnblogs.com/charlesliu " , CallbackContract  =   typeof (IMessagingServiceCallback))]
    
public   interface  IMessagingService
    {
        [OperationContract(IsOneWay
= false )]
        
void  SendMessage( string  message);
    }

    
public   interface  IMessagingServiceCallback
    {
        [OperationContract(IsOneWay
= true )]
        
void  MessageNotification( string  message);
    }

    [ServiceBehavior(UseSynchronizationContext
= true , ConcurrencyMode = ConcurrencyMode.Reentrant)]
    
public   class  MessagingService : IMessagingService
    {
        
public   void  SendMessage( string  message)
        {
            IMessagingServiceCallback callback 
=  OperationContext.Current.GetCallbackChannel < IMessagingServiceCallback > ();
            MessageBox.Show(String.Format(
" Message '{0}' received on thread {1} : MessageLoop = {2} " , message, Thread.CurrentThread.GetHashCode(), Application.MessageLoop),  " MessagingService.SendMessage() " );
            callback.MessageNotification(
string .Format( " MessagingService received message at {0} " , DateTime.Now.ToLongTimeString()));
        }
    }
}

 

Windows host 代码
//  © 2007 Michele Leroux Bustamante. All rights reserved 
//  Book: Learning WCF, O'Reilly
//  Book Blog: www.thatindigogirl.com
//  Michele's Blog: www.dasblonde.net
//  IDesign: www.idesign.net

using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Drawing;
using  System.Text;
using  System.Windows.Forms;
using  System.ServiceModel;
using  System.Threading;

namespace  WindowsHost
{
    
public   partial   class  Form1 : Form
    {
        ServiceHost m_serviceHost;

        
public  Form1()
        {
            InitializeComponent();
            
this .button2.Enabled = false ;

            m_serviceHost 
=   new  ServiceHost( typeof (Messaging.MessagingService));
        }

        
private   void  button1_Click( object  sender, EventArgs e)
        {
            
this .button1.Enabled  =   false ;
            
this .button2.Enabled  =   true ;
            m_serviceHost.Open();
        }

        
private   void  button2_Click( object  sender, EventArgs e)
        {
            
this .button1.Enabled  =   true ;
            
this .button2.Enabled  =   false ;
            m_serviceHost.Close();
        }

        
private   void  Form1_FormClosing( object  sender, FormClosingEventArgs e)
        {
            DialogResult result 
=  MessageBox.Show( " Are you sure you want to close the service? " " Service Controller " , MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);

            
if  (result  ==  DialogResult.Yes)
            {
                
if  (m_serviceHost  !=   null )
                {
                    m_serviceHost.Close();
                    m_serviceHost 
=   null ;
                }
            }
            
else
                e.Cancel
= true ;
        }

        
private   void  Form1_Load( object  sender, EventArgs e)
        {
            
this .Text  +=   " : UI Thread  "   +  Thread.CurrentThread.GetHashCode();
        }


    }
}

 

Host config代码
<? xml version="1.0" encoding="utf-8"  ?>
< configuration >
  
< system.web >
    
< compilation  debug ="true"   />
  
</ system.web >
  
<!--  When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. 
-->
  
< system.serviceModel >
    
< services >
      
< service  name ="Messaging.MessagingService"  behaviorConfiguration ="Messaging.Service1Behavior" >
        
< host >
          
< baseAddresses >
            
< add  baseAddress  = "net.tcp://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/"   />
            
< add  baseAddress  = "http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/"   />
          
</ baseAddresses >
        
</ host >
        
<!--  Service Endpoints  -->
        
<!--  Unless fully qualified, address is relative to base address supplied above  -->
        
< endpoint  address  =""  binding ="netTcpBinding"  contract ="Messaging.IMessagingService" >
          
<!--  
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          
-->
          
< identity >
            
< dns  value ="localhost" />
          
</ identity >
        
</ endpoint >
        
<!--  Metadata Endpoints  -->
        
<!--  The Metadata Exchange endpoint is used by the service to describe itself to clients.  -->
        
<!--  This endpoint does not use a secure binding and should be secured or removed before deployment  -->
        
< endpoint  address ="mex"  binding ="mexHttpBinding"  contract ="IMetadataExchange" />
      
</ service >
    
</ services >
    
< behaviors >
      
< serviceBehaviors >
        
< behavior  name ="Messaging.Service1Behavior" >
          
<!--  To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment 
-->
          
< serviceMetadata  httpGetEnabled ="True" />
          
<!--  To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information 
-->
          
< serviceDebug  includeExceptionDetailInFaults ="False"   />
        
</ behavior >
      
</ serviceBehaviors >
    
</ behaviors >
  
</ system.serviceModel >
</ configuration >

 

Client 代码
using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Drawing;
using  System.Linq;
using  System.Text;
using  System.Windows.Forms;
using  System.Threading;
using  WindowsClient.ServiceReference1;
using  System.ServiceModel;

namespace  WindowsClient
{
    
public   partial   class  Form1 : Form
    {
        ServiceReference1.MessagingServiceClient proxy;

        
public  Form1()
        {
            InitializeComponent();
            
this .Text  +=   " : ThreadId  "   +  Thread.CurrentThread.GetHashCode().ToString();
            MessagingServiceCallback callbackType 
=   new  MessagingServiceCallback();
            InstanceContext context 
=   new  InstanceContext(callbackType);
            proxy 
=   new  MessagingServiceClient(context);

            
        }

        
private   void  button1_Click( object  sender, EventArgs e)
        {
            proxy.SendMessage(
string .Format( " Hello from {0}  " this .Text));
        }
    }

    [CallbackBehavior(UseSynchronizationContext 
=   false )]
    
internal   class  MessagingServiceCallback : IMessagingServiceCallback
    {
        
#region  IMessagingServiceCallback Members

        
public   void  MessageNotification( string  message)
        {
            MessageBox.Show(String.Format(
" Message '{0}' received on thread {1} : MessageLoop = {2} " , message, Thread.CurrentThread.GetHashCode(), Application.MessageLoop),  " IMessagingServiceCallback.MessageNotification() " );
        }

        
#endregion
    }
}

 

Windows服务宿主

• 用于无人值守的服务器主机
• 也可以部署在客户端主机
– 需要对Windows服务进行额外的配置
• 当主机启动时,宿主环境初始化,如果发生错误可以重新启动
• 打开与关闭Windows服务时,ServiceHost实例也会被打开与关闭

Demo:

 

Service 代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.ServiceModel;
using  System.Threading;
using  System.Windows.Forms;

namespace  Messaging
{
    [ServiceContract(Namespace 
=   " http://www.cnblogs.com/charlesliu " )]
    
public   interface  IMessagingService
    {
        [OperationContract]
        
string  SendMessage( string  message);
    }

    [ServiceBehavior(UseSynchronizationContext
= false )]
    
public   class  MessagingService : IMessagingService
    {
        
public   string  SendMessage( string  message)
        {
            
return  String.Format( " Message '{0}' received on thread {1} " , message, Thread.CurrentThread.GetHashCode());
        }
    }
}

 

 

Windows Service Host代码
using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Diagnostics;
using  System.Linq;
using  System.ServiceProcess;
using  System.Text;
using  System.ServiceModel;
using  Messaging;

namespace  WindowsServiceHost
{
    
public   partial   class  ServiceHost : ServiceBase
    {
        System.ServiceModel.ServiceHost host;

        
public  ServiceHost()
        {
            InitializeComponent();
            
this .ServiceName  =   " MessageServiceHost_EventLog " ;
        }

        
protected   override   void  OnStart( string [] args)
        {
            host 
=   new  System.ServiceModel.ServiceHost( typeof (MessagingService));
            host.Faulted
+= new  EventHandler(host_Faulted);
            host.Open();

            
string  baseAddresses  =   "" ;
            
foreach  (Uri address  in  host.BaseAddresses)
            {
                baseAddresses 
+=   "   "   +  address.AbsoluteUri;
            }
            
string  s  =  String.Format( " {0} listening at {1} " this .ServiceName, baseAddresses);
            
this .EventLog.WriteEntry(s, EventLogEntryType.Information);
        }

        
void  host_Faulted( object  sender, EventArgs e)
        {
            
string  s  =  String.Format( " {0} has faulted, notify administrators of this problem " this .ServiceName);
            
this .EventLog.WriteEntry(s, EventLogEntryType.Error);
        }

        
protected   override   void  OnStop()
        {
            
if  (host  !=   null )
            {
                host.Close();
                
string  s  =  String.Format( " {0} stopped " this .ServiceName);
                
this .EventLog.WriteEntry(s, EventLogEntryType.Information);
            }

            host 
=   null ;
        }
    }
}

 

 

Host config 代码
<? xml version="1.0" encoding="utf-8"  ?>
< configuration >
  
< system.web >
    
< compilation  debug ="true"   />
  
</ system.web >
  
<!--  When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. 
-->
  
< system.serviceModel >
    
< services >
      
< service  name ="Messaging.MessagingService"  behaviorConfiguration ="Messaging.Service1Behavior" >
        
< host >
          
< baseAddresses >
            
< add  baseAddress  = "net.tcp://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/"   />
            
< add  baseAddress  = "http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/"   />
          
</ baseAddresses >
        
</ host >
        
<!--  Service Endpoints  -->
        
<!--  Unless fully qualified, address is relative to base address supplied above  -->
        
< endpoint  address  =""  binding ="netTcpBinding"  contract ="Messaging.IMessagingService" >
          
<!--  
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          
-->
          
< identity >
            
< dns  value ="localhost" />
          
</ identity >
        
</ endpoint >
        
<!--  Metadata Endpoints  -->
        
<!--  The Metadata Exchange endpoint is used by the service to describe itself to clients.  -->
        
<!--  This endpoint does not use a secure binding and should be secured or removed before deployment  -->
        
< endpoint  address ="mex"  binding ="mexHttpBinding"  contract ="IMetadataExchange" />
      
</ service >
    
</ services >
    
< behaviors >
      
< serviceBehaviors >
        
< behavior  name ="Messaging.Service1Behavior" >
          
<!--  To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment 
-->
          
< serviceMetadata  httpGetEnabled ="True" />
          
<!--  To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information 
-->
          
< serviceDebug  includeExceptionDetailInFaults ="False"   />
        
</ behavior >
      
</ serviceBehaviors >
    
</ behaviors >
  
</ system.serviceModel >
</ configuration >

 

对于Windows Service的开发这里不详细探讨,可以参考其他学习资料。其实很简单,上边的就是核心代码,写完后再设计窗口右键鼠标,选择Add Installer,会自动产生安装类。然后再VS2008.NET Command窗口,用installutil servicename.exe (/u)来安装和写在Windows Service, 然后就可以在管理工具/Service下找到这个Service了,开启服务后,Client就可以调用WCF服务了。

 

Client 代码
using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Drawing;
using  System.Linq;
using  System.Text;
using  System.Windows.Forms;
using  System.Threading;
using  WindowsClient.ServiceReference1;
using  System.ServiceModel;

namespace  WindowsClient
{
    
public   partial   class  Form1 : Form
    {
        ServiceReference1.MessagingServiceClient proxy;

        
public  Form1()
        {
            InitializeComponent();
            
this .Text  +=   " : ThreadId  "   +  Thread.CurrentThread.GetHashCode().ToString();

            proxy 
=   new  MessagingServiceClient();

            
        }

        
private   void  button1_Click( object  sender, EventArgs e)
        {
            MessageBox.Show(proxy.SendMessage(
string .Format( " Hello from {0}  " this .Text)));
        }
    }
}

 

 

宿主的应用场景
• 在每个平台上选择不同类型的宿主
• Windows Server® 2003
– 在IIS 6上应用HTTP协议
– 在Windows服务上应用non-HTTP
• Windows Server® 2008(Longhorn)
– 在IIS 7/WAS可以应用所有的协议
• Windows® XP Service Pack 2 与Windows Vista®
– 客户端主机运行Windows应用程序,或者Windows服务

(完)

你可能感兴趣的:(host)