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 on 2011-09-08 11:40  贵贵  阅读(552)  评论(1编辑  收藏  举报