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实例化为服务类型

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

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

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

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

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,然后启用即可,一般都可以搞定。

唯一不太一样的地方是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的配置方法,本人搞了半天才研究明白。

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

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

 

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

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

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

有些东西要知道:

[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服务

(完)

posted @ 2010-03-29 17:30  烟鬼  阅读(8405)  评论(1编辑  收藏  举报