다음을 통해 공유


방법: 관리되는 Windows 서비스에서 WCF 서비스 호스팅

이 항목에서는 Windows 서비스에서 호스트하는 WCF(Windows Communication Foundation) 서비스를 만드는 데 필요한 기본 단계에 대해 간략하게 설명합니다. 관리형 Windows 서비스 호스팅 옵션을 통해 사용할 수 있는 시나리오는 메시지가 활성화되지 않은 보안 환경의 IIS(인터넷 정보 서비스) 외부에서 호스트되는 장기 실행 WCF 서비스입니다. 서비스 수명은 대신 운영 체제에 의해 제어됩니다. 모든 버전의 Windows에서 이 호스팅 옵션을 사용할 수 있습니다.

Windows 서비스는 MMC(Microsoft Management Console)의 Microsoft.ManagementConsole.SnapIn을 통해 관리되고 시스템 부팅 시 자동으로 시작되도록 구성될 수 있습니다. 이 호스팅 옵션은 서비스를 관리형 Windows 서비스로 호스트하는 애플리케이션 도메인(AppDomain) 등록으로 구성되므로 서비스의 프로세스 수명은 Windows 서비스의 SCM(서비스 제어 관리자)에 의해 제어됩니다.

서비스 코드에는 서비스 계약에 대한 서비스 구현, Windows 서비스 클래스 및 설치 관리자 클래스가 포함됩니다. 서비스 구현 클래스인 CalculatorService는 WCF 서비스입니다. CalculatorWindowsService는 Windows 서비스입니다. Windows 서비스로 정규화하기 위해 클래스가 ServiceBase에서 상속되고 OnStartOnStop 메서드를 구현합니다. OnStart에서 ServiceHost 형식에 대한 CalculatorService가 만들어지고 열립니다. OnStop에서 서비스가 중지되고 삭제됩니다. 또한 호스트는 애플리케이션 설정에서 구성된 대로 기본 주소를 서비스 호스트에 제공합니다. Installer에서 상속되는 설치 관리자 클래스를 사용하면 프로그램을 Installutil.exe 도구를 통해 Windows 서비스로 설치할 수 있습니다.

서비스 생성 및 호스팅 코드 제공

  1. Service라는 새 Visual Studio 콘솔 앱 프로젝트를 만듭니다.

  2. Program.cs의 이름을 Service.cs로 바꿉니다.

  3. 네임스페이스를 Microsoft.ServiceModel.Samples로 변경합니다.

  4. 다음 어셈블리에 대한 참조를 추가합니다.

    • System.ServiceModel.dll
    • System.ServiceProcess.dll
    • System.Configuration.Install.dll
  5. 다음 using 지시문을 Service.cs 추가합니다.

    using System.ComponentModel;
    using System.ServiceModel;
    using System.ServiceProcess;
    using System.Configuration;
    using System.Configuration.Install;
    
    Imports System.ComponentModel
    Imports System.ServiceModel
    Imports System.ServiceProcess
    Imports System.Configuration
    Imports System.Configuration.Install
    
  6. 다음 코드와 같이 ICalculator 서비스 계약을 정의합니다.

    // Define a service contract.
    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
    public interface ICalculator
    {
        [OperationContract]
        double Add(double n1, double n2);
        [OperationContract]
        double Subtract(double n1, double n2);
        [OperationContract]
        double Multiply(double n1, double n2);
        [OperationContract]
        double Divide(double n1, double n2);
    }
    
    ' Define a service contract.
    <ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
    Public Interface ICalculator
        <OperationContract()> _
        Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double
    End Interface
    
  7. 다음 코드와 같이 CalculatorService 클래스에서 서비스 계약을 구현합니다.

    // Implement the ICalculator service contract in a service class.
    public class CalculatorService : ICalculator
    {
        // Implement the ICalculator methods.
        public double Add(double n1, double n2)
        {
            double result = n1 + n2;
            return result;
        }
    
        public double Subtract(double n1, double n2)
        {
            double result = n1 - n2;
            return result;
        }
    
        public double Multiply(double n1, double n2)
        {
            double result = n1 * n2;
            return result;
        }
    
        public double Divide(double n1, double n2)
        {
            double result = n1 / n2;
            return result;
        }
    }
    
    ' Implement the ICalculator service contract in a service class.
    Public Class CalculatorService
        Implements ICalculator
        ' Implement the ICalculator methods.
        Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Add
            Return n1 + n2
    
        End Function
    
        Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Subtract
            Return n1 - n2
    
        End Function
    
        Public Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Multiply
            Return n1 * n2
        End Function
    
        Public Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Divide
            Return n1 / n2
    
        End Function
    End Class
    
  8. CalculatorWindowsService 클래스에서 상속되는 ServiceBase 클래스를 새로 만듭니다. serviceHost 인스턴스를 참조하는 ServiceHost 로컬 변수를 추가합니다. Main을 호출하는 ServiceBase.Run(new CalculatorWindowsService) 메서드를 정의합니다.

    public class CalculatorWindowsService : ServiceBase
    {
        public ServiceHost serviceHost = null;
        public CalculatorWindowsService()
        {
            // Name the Windows Service
            ServiceName = "WCFWindowsServiceSample";
        }
    
        public static void Main()
        {
            ServiceBase.Run(new CalculatorWindowsService());
        }
    
    Public Class CalculatorWindowsService
        Inherits ServiceBase
        Public serviceHost As ServiceHost = Nothing
        Public Sub New()
            ' Name the Windows Service
            ServiceName = "WCFWindowsServiceSample"
        End Sub
    
        Public Shared Sub Main()
            ServiceBase.Run(New CalculatorWindowsService())
        End Sub
    
  9. 다음 코드와 같이 새 OnStart(String[]) 인스턴스를 만든 다음 열어서 ServiceHost 메서드를 재정의합니다.

    // Start the Windows service.
    protected override void OnStart(string[] args)
    {
        if (serviceHost != null)
        {
            serviceHost.Close();
        }
    
        // Create a ServiceHost for the CalculatorService type and
        // provide the base address.
        serviceHost = new ServiceHost(typeof(CalculatorService));
    
        // Open the ServiceHostBase to create listeners and start
        // listening for messages.
        serviceHost.Open();
    }
    
    ' Start the Windows service.
    Protected Overrides Sub OnStart(ByVal args() As String)
        If serviceHost IsNot Nothing Then
            serviceHost.Close()
        End If
    
        ' Create a ServiceHost for the CalculatorService type and 
        ' provide the base address.
        serviceHost = New ServiceHost(GetType(CalculatorService))
    
        ' Open the ServiceHostBase to create listeners and start 
        ' listening for messages.
        serviceHost.Open()
    End Sub
    
  10. 다음 코드와 같이 OnStop를 닫는 ServiceHost 메서드를 재정의합니다.

    protected override void OnStop()
    {
        if (serviceHost != null)
        {
            serviceHost.Close();
            serviceHost = null;
        }
    }
    
    Protected Overrides Sub OnStop()
        If serviceHost IsNot Nothing Then
            serviceHost.Close()
            serviceHost = Nothing
        End If
    End Sub
    
  11. ProjectInstaller에서 상속되며 Installer로 설정된 RunInstallerAttribute로 표시되는 true 클래스를 새로 만듭니다. 그러면 Installutil.exe 도구를 통해 Windows 서비스를 설치할 수 있습니다.

    // Provide the ProjectInstaller class which allows
    // the service to be installed by the Installutil.exe tool
    [RunInstaller(true)]
    public class ProjectInstaller : Installer
    {
        private ServiceProcessInstaller process;
        private ServiceInstaller service;
    
        public ProjectInstaller()
        {
            process = new ServiceProcessInstaller();
            process.Account = ServiceAccount.LocalSystem;
            service = new ServiceInstaller();
            service.ServiceName = "WCFWindowsServiceSample";
            Installers.Add(process);
            Installers.Add(service);
        }
    }
    
    ' Provide the ProjectInstaller class which allows 
    ' the service to be installed by the Installutil.exe tool
    <RunInstaller(True)> _
    Public Class ProjectInstaller
        Inherits Installer
        Private process As ServiceProcessInstaller
        Private service As ServiceInstaller
    
        Public Sub New()
            process = New ServiceProcessInstaller()
            process.Account = ServiceAccount.LocalSystem
            service = New ServiceInstaller()
            service.ServiceName = "WCFWindowsServiceSample"
            Installers.Add(process)
            Installers.Add(service)
        End Sub
    End Class
    
  12. 프로젝트를 만들 때 생성된 Service 클래스를 제거합니다.

  13. 프로젝트에 애플리케이션 구성 파일을 추가합니다. 파일의 내용을 다음 구성 XML로 바꿉니다.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>    <services>
          <!-- This section is optional with the new configuration model
               introduced in .NET Framework 4. -->
          <service name="Microsoft.ServiceModel.Samples.CalculatorService"
                   behaviorConfiguration="CalculatorServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8000/ServiceModelSamples/service"/>
              </baseAddresses>
            </host>
            <!-- this endpoint is exposed at the base address provided by host: http://localhost:8000/ServiceModelSamples/service  -->
            <endpoint address=""
                      binding="wsHttpBinding"
                      contract="Microsoft.ServiceModel.Samples.ICalculator" />
            <!-- the mex endpoint is exposed at http://localhost:8000/ServiceModelSamples/service/mex -->
            <endpoint address="mex"
                      binding="mexHttpBinding"
                      contract="IMetadataExchange" />
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="CalculatorServiceBehavior">
              <serviceMetadata httpGetEnabled="true"/>
              <serviceDebug includeExceptionDetailInFaults="False"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    </configuration>
    

    솔루션 탐색기에서 App.config 파일을 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다. 출력 디렉터리에 복사 아래에서 변경된 내용만 복사를 선택합니다.

    다음 예제에서는 구성 파일의 엔드포인트를 명시적으로 지정합니다. 서비스에 엔드포인트를 추가하지 않으면 런타임에서 기본 엔드포인트를 자동으로 추가합니다. 이 예제에서는 서비스의 ServiceMetadataBehaviortrue로 설정되어 있으므로 서비스의 메타데이터 게시 기능도 사용하도록 설정되었습니다. 기본 엔드포인트, 바인딩 및 동작에 대한 자세한 내용은 단순화된 구성WCF 서비스를 위한 단순화된 구성을 참조하세요.

서비스를 설치하고 실행합니다.

  1. 솔루션을 빌드하여 Service.exe 실행 파일을 만듭니다.

  2. Visual Studio용 개발자 명령 프롬프트를 열고 프로젝트 디렉터리로 이동합니다. 명령 프롬프트에서 installutil bin\service.exe를 입력하여 Windows 서비스를 설치합니다.

    명령 프롬프트에서 services.msc를 입력하여 SCM(서비스 제어 관리자)에 액세스합니다. Windows 서비스는 서비스에서 "WCFWindowsServiceSample"로 표시되어야 합니다. WCF 서비스는 Windows 서비스가 실행 중인 경우에만 클라이언트에 응답할 수 있습니다. 서비스를 시작하려면 SCM에서 해당 서비스를 마우스 오른쪽 단추로 클릭하고 “시작”을 선택하거나 명령 프롬프트에서 net start WCFWindowsServiceSample을 입력합니다.

  3. 서비스를 변경하려면 먼저 서비스를 중지하고 제거해야 합니다. 서비스를 중지하려면 SCM에서 해당 서비스를 마우스 오른쪽 단추로 클릭하고 “중지”를 선택하거나 명령 프롬프트에서 type net stop WCFWindowsServiceSample을 입력합니다. Windows 서비스를 중지한 다음 클라이언트를 실행할 경우 클라이언트가 서비스에 액세스하려고 할 때 EndpointNotFoundException 예외가 발생합니다. Windows 서비스를 제거하려면 명령 프롬프트에서 installutil /u bin\service .exe를 입력합니다.

예시

다음은 이 항목에서 사용되는 전체 코드 목록입니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ComponentModel;
using System.ServiceModel;
using System.ServiceProcess;
using System.Configuration;
using System.Configuration.Install;

namespace Microsoft.ServiceModel.Samples
{
    // Define a service contract.
    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
    public interface ICalculator
    {
        [OperationContract]
        double Add(double n1, double n2);
        [OperationContract]
        double Subtract(double n1, double n2);
        [OperationContract]
        double Multiply(double n1, double n2);
        [OperationContract]
        double Divide(double n1, double n2);
    }

    // Implement the ICalculator service contract in a service class.
    public class CalculatorService : ICalculator
    {
        // Implement the ICalculator methods.
        public double Add(double n1, double n2)
        {
            double result = n1 + n2;
            return result;
        }

        public double Subtract(double n1, double n2)
        {
            double result = n1 - n2;
            return result;
        }

        public double Multiply(double n1, double n2)
        {
            double result = n1 * n2;
            return result;
        }

        public double Divide(double n1, double n2)
        {
            double result = n1 / n2;
            return result;
        }
    }

    public class CalculatorWindowsService : ServiceBase
    {
        public ServiceHost serviceHost = null;
        public CalculatorWindowsService()
        {
            // Name the Windows Service
            ServiceName = "WCFWindowsServiceSample";
        }

        public static void Main()
        {
            ServiceBase.Run(new CalculatorWindowsService());
        }

        // Start the Windows service.
        protected override void OnStart(string[] args)
        {
            if (serviceHost != null)
            {
                serviceHost.Close();
            }

            // Create a ServiceHost for the CalculatorService type and
            // provide the base address.
            serviceHost = new ServiceHost(typeof(CalculatorService));

            // Open the ServiceHostBase to create listeners and start
            // listening for messages.
            serviceHost.Open();
        }

        protected override void OnStop()
        {
            if (serviceHost != null)
            {
                serviceHost.Close();
                serviceHost = null;
            }
        }
    }

    // Provide the ProjectInstaller class which allows
    // the service to be installed by the Installutil.exe tool
    [RunInstaller(true)]
    public class ProjectInstaller : Installer
    {
        private ServiceProcessInstaller process;
        private ServiceInstaller service;

        public ProjectInstaller()
        {
            process = new ServiceProcessInstaller();
            process.Account = ServiceAccount.LocalSystem;
            service = new ServiceInstaller();
            service.ServiceName = "WCFWindowsServiceSample";
            Installers.Add(process);
            Installers.Add(service);
        }
    }
}
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text

Imports System.ComponentModel
Imports System.ServiceModel
Imports System.ServiceProcess
Imports System.Configuration
Imports System.Configuration.Install

Namespace Microsoft.ServiceModel.Samples
    ' Define a service contract.
    <ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
    Public Interface ICalculator
        <OperationContract()> _
        Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double
        <OperationContract()> _
        Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double
    End Interface

    ' Implement the ICalculator service contract in a service class.
    Public Class CalculatorService
        Implements ICalculator
        ' Implement the ICalculator methods.
        Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Add
            Return n1 + n2

        End Function

        Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Subtract
            Return n1 - n2

        End Function

        Public Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Multiply
            Return n1 * n2
        End Function

        Public Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Divide
            Return n1 / n2

        End Function
    End Class

    Public Class CalculatorWindowsService
        Inherits ServiceBase
        Public serviceHost As ServiceHost = Nothing
        Public Sub New()
            ' Name the Windows Service
            ServiceName = "WCFWindowsServiceSample"
        End Sub

        Public Shared Sub Main()
            ServiceBase.Run(New CalculatorWindowsService())
        End Sub

        ' Start the Windows service.
        Protected Overrides Sub OnStart(ByVal args() As String)
            If serviceHost IsNot Nothing Then
                serviceHost.Close()
            End If

            ' Create a ServiceHost for the CalculatorService type and 
            ' provide the base address.
            serviceHost = New ServiceHost(GetType(CalculatorService))

            ' Open the ServiceHostBase to create listeners and start 
            ' listening for messages.
            serviceHost.Open()
        End Sub

        Protected Overrides Sub OnStop()
            If serviceHost IsNot Nothing Then
                serviceHost.Close()
                serviceHost = Nothing
            End If
        End Sub
    End Class
    ' Provide the ProjectInstaller class which allows 
    ' the service to be installed by the Installutil.exe tool
    <RunInstaller(True)> _
    Public Class ProjectInstaller
        Inherits Installer
        Private process As ServiceProcessInstaller
        Private service As ServiceInstaller

        Public Sub New()
            process = New ServiceProcessInstaller()
            process.Account = ServiceAccount.LocalSystem
            service = New ServiceInstaller()
            service.ServiceName = "WCFWindowsServiceSample"
            Installers.Add(process)
            Installers.Add(service)
        End Sub
    End Class
End Namespace

"자체 호스팅" 옵션과 같이 Windows 서비스 호스팅 환경에서는 일부 호스팅 코드를 애플리케이션의 일부로 작성해야 합니다. 서비스는 콘솔 애플리케이션으로 구현되고 자체 호스팅 코드가 포함됩니다. 다른 호스팅 환경의 경우 IIS(인터넷 정보 서비스)의 WAS(Windows Process Activation Service) 호스팅과 같이 개발자가 호스팅 코드를 작성할 필요가 없습니다.

참고 항목