다음을 통해 공유


데이터 전송 아키텍처 개요

WCF(Windows Communication Foundation)는 메시징 인프라로 간주할 수 있습니다. 메시지를 수신하고, 처리하고, 추가 작업을 위해 사용자 코드로 디스패치하거나, 사용자 코드에서 제공한 데이터에서 메시지를 생성하여 대상으로 배달할 수 있습니다. 고급 개발자를 위한 이 항목에서는 메시지 및 포함된 데이터를 처리하기 위한 아키텍처에 대해 설명합니다. 데이터를 보내고 받는 방법에 대한 작업 지향적인 간단한 보기는 서비스 계약에서 데이터 전송 지정을 참조하세요.

비고

이 항목에서는 WCF 개체 모델을 검사하여 표시되지 않는 WCF 구현 세부 정보를 설명합니다. 문서화된 구현 세부 정보와 관련하여 두 가지 주의 사항이 있습니다. 먼저 설명이 간소화됩니다. 최적화 또는 기타 이유로 인해 실제 구현이 더 복잡할 수 있습니다. 둘째, 버전에서 버전으로 또는 서비스 릴리스에서도 예고 없이 변경될 수 있으므로 문서화된 구현 세부 정보도 특정 구현 세부 정보에 의존해서는 안 됩니다.

기본 아키텍처

WCF 메시지 처리 기능의 핵심은 Message메시지 클래스 사용에 자세히 설명된 클래스입니다. WCF의 런타임 구성 요소는 채널 스택과 서비스 프레임워크의 두 가지 주요 부분으로 나눌 수 있으며 클래스는 Message 연결점입니다.

채널 스택은 유효한 Message 인스턴스와 메시지 데이터의 송신 또는 수신에 해당하는 일부 작업 간에 변환을 담당합니다. 송신 쪽에서 채널 스택은 유효한 Message 인스턴스를 사용하고 일부 처리 후 메시지 전송에 논리적으로 해당하는 몇 가지 작업을 수행합니다. 이 작업은 TCP 또는 HTTP 패킷을 보내고, 메시지 큐에서 메시지를 큐에 대기하고, 데이터베이스에 메시지를 쓰고, 파일 공유에 저장하거나, 구현에 따라 다른 작업을 수행할 수 있습니다. 가장 일반적인 작업은 네트워크 프로토콜을 통해 메시지를 보내는 것입니다. 수신 쪽에서는 반대로 동작이 검색되고(TCP 또는 HTTP 패킷이 도착하거나 다른 동작일 수 있음) 처리 후 채널 스택이 이 작업을 유효한 Message 인스턴스로 변환합니다.

클래스 및 채널 스택을 Message 직접 사용하여 WCF를 사용할 수 있습니다. 그러나 이렇게 하는 것은 어렵고 시간이 많이 걸립니다. 또한 개체는 Message 메타데이터 지원을 제공하지 않으므로 이러한 방식으로 WCF를 사용하는 경우 강력한 형식의 WCF 클라이언트를 생성할 수 없습니다.

따라서 WCF에는 개체를 생성하고 수신 Message 하는 데 사용할 수 있는 사용하기 쉬운 프로그래밍 모델을 제공하는 서비스 프레임워크가 포함되어 있습니다. 서비스 프레임워크는 서비스 계약의 개념을 통해 서비스를 .NET Framework 형식에 매핑하고 특성으로 OperationContractAttribute 표시된 .NET Framework 메서드인 사용자 작업에 메시지를 디스패치합니다(자세한 내용은 서비스 계약 디자인 참조). 이러한 메서드에는 매개 변수 및 반환 값이 있을 수 있습니다. 서비스 쪽에서 서비스 프레임워크는 들어오는 Message 인스턴스를 매개 변수로 변환하고 반환 값을 나가는 Message 인스턴스로 변환합니다. 클라이언트 쪽에서는 반대의 작업을 수행합니다. 예를 들어 아래의 FindAirfare 작업을 고려해 보세요.

[ServiceContract]
public interface IAirfareFinderService
{
    [OperationContract]
    int FindAirfare(string FromCity, string ToCity, out bool IsDirectFlight);
}
<ServiceContract()> _
Public Interface IAirfareFinderService

    <OperationContract()> _
    Function FindAirfare(ByVal FromCity As String, _
    ByVal ToCity As String, ByRef IsDirectFlight As Boolean) As Integer

End Interface

클라이언트에서 호출되는 경우를 가정해 보겠습니다 FindAirfare . 클라이언트의 서비스 프레임워크는 FromCityToCity 매개 변수를 전송할 Message 인스턴스로 변환하고, 이를 전송하기 위해 채널 스택에 전달합니다.

서비스 측에서 Message 인스턴스가 채널 스택에서 도착하면, 서비스 프레임워크는 메시지에서 관련 데이터를 추출하여 FromCityToCity 매개 변수를 채우고 서비스 측 FindAirfare 메서드를 호출합니다. 메서드가 반환되면 서비스 프레임워크는 반환된 정수 값과 출력 매개 변수를 IsDirectFlight 가져와 이 정보를 포함하는 개체 인스턴스를 Message 만듭니다. 그런 다음 인스턴스를 Message 채널 스택에 전달하여 클라이언트로 다시 보냅니다.

클라이언트 쪽 Message 에서 응답 메시지가 포함된 인스턴스가 채널 스택에서 나타납니다. 서비스 프레임워크는 반환 값과 IsDirectFlight 값을 추출하고 클라이언트의 호출자에게 반환합니다.

메시지 클래스

클래스 Message 는 메시지의 추상적 표현을 위한 것이지만 해당 디자인은 SOAP 메시지에 강력하게 연결됩니다. A Message 에는 메시지 본문, 메시지 헤더 및 메시지 속성의 세 가지 주요 정보가 포함됩니다.

메시지 본문

메시지 본문은 메시지의 실제 데이터 페이로드를 나타내기 위한 것입니다. 메시지 본문은 항상 XML Infoset으로 표시됩니다. 그렇다고 해서 WCF에서 만들거나 받은 모든 메시지가 XML 형식이어야 한다는 의미는 아닙니다. 메시지 본문을 해석하는 방법을 결정하는 것은 채널 스택에 달려 있습니다. XML로 내보내거나, 다른 형식으로 변환하거나, 완전히 생략할 수도 있습니다. 물론 WCF가 제공하는 대부분의 바인딩에서 메시지 본문은 SOAP 봉투의 본문 섹션에서 XML 콘텐츠로 표시됩니다.

Message 클래스가 본문을 나타내는 XML 데이터로 구성된 버퍼를 반드시 포함하는 것이 아니라는 점을 이해하는 것이 중요합니다. 논리적으로 XML Message Infoset을 포함하지만 이 Infoset은 동적으로 생성될 수 있으며 메모리에 실제로 존재하지 않을 수 있습니다.

메시지 본문에 데이터 배치

데이터를 메시지 본문에 넣는 균일한 메커니즘은 없습니다. 클래스는 Message를 매개변수로 받는 추상 메서드 OnWriteBodyContents(XmlDictionaryWriter)를 가지고 있습니다. 클래스의 Message 각 하위 클래스는 이 메서드를 재정의하고 자체 콘텐츠를 작성합니다. 메시지 본문에는 논리적으로 생성되는 XML Infoset이 OnWriteBodyContent 포함됩니다. 예를 들어 다음 Message 하위 클래스를 고려합니다.

public class AirfareRequestMessage : Message
{
    public string fromCity = "Tokyo";
    public string toCity = "London";
    //code omitted…
    protected override void OnWriteBodyContents(XmlDictionaryWriter w)
    {
        w.WriteStartElement("airfareRequest");
        w.WriteElementString("from", fromCity);
        w.WriteElementString("to", toCity);
        w.WriteEndElement();
    }

    public override MessageVersion Version
    {
        get { throw new NotImplementedException("The method is not implemented.") ; }
    }

    public override MessageProperties Properties
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }
    public override MessageHeaders Headers
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }

    public override bool IsEmpty
    {
        get
        {
            return base.IsEmpty;
        }
    }

    public override bool IsFault
    {
        get
        {
            return base.IsFault;
        }
    }
}
Public Class AirfareRequestMessage
    Inherits Message

    Public fromCity As String = "Tokyo"
    Public toCity As String = "London"
    ' Code omitted…
    Protected Overrides Sub OnWriteBodyContents(ByVal w As XmlDictionaryWriter)
        w.WriteStartElement("airfareRequest")
        w.WriteElementString("from", fromCity)
        w.WriteElementString("to", toCity)
        w.WriteEndElement()
    End Sub

    Public Overrides ReadOnly Property Version() As MessageVersion
        Get
            Throw New NotImplementedException("The method is not implemented.")
        End Get
    End Property

    Public Overrides ReadOnly Property Properties() As MessageProperties
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property

    Public Overrides ReadOnly Property Headers() As MessageHeaders
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property


    Public Overrides ReadOnly Property IsEmpty() As Boolean
        Get
            Return MyBase.IsEmpty
        End Get
    End Property

    Public Overrides ReadOnly Property IsFault() As Boolean
        Get
            Return MyBase.IsFault
        End Get
    End Property
End Class

실제로 인스턴스에는 AirfareRequestMessage 두 개의 문자열("fromCity" 및 "toCity")만 포함됩니다. 그러나 논리적으로 메시지에는 다음 XML 정보 세트가 포함됩니다.

<airfareRequest>  
    <from>Tokyo</from>  
    <to>London</to>  
</airfareRequest>  

물론 서비스 프레임워크를 사용하여 작업 계약 매개 변수에서 이전 메시지와 같은 메시지를 만들 수 있으므로 일반적으로 이러한 방식으로 메시지를 만들지 않습니다. Message 또한 클래스에는 일반적인 형식의 콘텐츠가 포함된 메시지를 만드는 데 사용할 수 있는 정적 CreateMessage 메서드가 있습니다. 빈 메시지, XML로 DataContractSerializer직렬화된 개체가 포함된 메시지, SOAP 오류가 포함된 메시지, XML이 XmlReader나타내는 메시지 등이 있습니다.

메시지 본문에서 데이터 가져오기

다음 두 가지 주요 방법으로 메시지 본문에 저장된 데이터를 추출할 수 있습니다.

  • 메서드 WriteBodyContents(XmlDictionaryWriter)를 호출하고 XML 기록기를 전달하여 전체 메시지 본문을 한꺼번에 가져올 수 있습니다. 전체 메시지 본문이 이 작가에게 기록됩니다. 전체 메시지 본문을 한 번에 가져오는 것을 메시지 작성이라고도 합니다. 쓰기는 주로 메시지를 보낼 때 채널 스택에 의해 수행됩니다. 채널 스택의 일부는 일반적으로 전체 메시지 본문에 액세스하여 인코딩하고 보냅니다.

  • 메시지 본문에서 정보를 가져오는 또 다른 방법은 XML 판독기를 호출 GetReaderAtBodyContents() 하고 가져오는 것입니다. 그러면 판독기에서 메서드를 호출하여 필요에 따라 메시지 본문에 순차적으로 액세스할 수 있습니다. 메시지 본문을 하나씩 가져오는 것을 메시지 읽기라고도합니다. 메시지 읽기는 메시지를 수신할 때 서비스 프레임워크에서 주로 사용됩니다. 예를 들어 DataContractSerializer 사용 중인 경우 서비스 프레임워크는 본문 위에 XML 판독기를 가져와 역직렬화 엔진에 전달합니다. 그러면 요소별로 메시지 요소를 읽고 해당 개체 그래프를 생성하기 시작합니다.

메시지 본문은 한 번만 검색할 수 있습니다. 이렇게 하면 앞으로 전용 스트림을 사용할 수 있습니다. 예를 들어, OnWriteBodyContents(XmlDictionaryWriter)를 작성하여 FileStream에서 읽고 결과를 XML Infoset으로 반환할 수 있습니다. 파일의 시작 부분으로 "되감기"할 필요가 없습니다.

WriteBodyContentsGetReaderAtBodyContents 메서드는 메시지 본문이 이전에 검색된 적이 없는지 간단히 확인하고, 그 후 각각 OnWriteBodyContents 또는 OnGetReaderAtBodyContents를 호출합니다.

WCF의 메시지 사용량

대부분의 메시지는 나가는 메시지(채널 스택에서 보낼 서비스 프레임워크에서 만든 메시지) 또는 들어오는 메시지(채널 스택에서 도착하여 서비스 프레임워크에 의해 해석되는 메시지)로 분류할 수 있습니다. 또한 채널 스택은 버퍼링 또는 스트리밍 모드에서 작동할 수 있습니다. 서비스 프레임워크는 스트리밍 또는 비스트림 프로그래밍 모델을 노출할 수도 있습니다. 이렇게 하면 구현에 대한 간소화된 세부 정보와 함께 다음 표에 나열된 사례가 발생합니다.

메시지 유형 메시지의 본문 데이터 Write(OnWriteBodyContents) 구현 읽기(OnGetReaderAtBodyContents) 구현
비스트림 프로그래밍 모델에서 생성된 아웃고잉 데이터 메시지 작성에 필요한 데이터(예: 개체 및 직렬화를 위해 필요한 DataContractSerializer 인스턴스)* 저장된 데이터를 기반으로 메시지를 작성하는 사용자 정의 논리(예: WriteObject에서 사용하는 serializer인 경우 DataContractSerializer를 호출) 호출 OnWriteBodyContents, 결과 버퍼링, 버퍼를 통해 XML 판독기 반환
스트리밍 프로그래밍 모델에서 생성된 아웃고잉 데이터 Stream 기록할 데이터와 함께* 메커니즘을 사용하여 IStreamProvider 저장된 스트림에서 데이터 쓰기* 호출 OnWriteBodyContents, 결과 버퍼링, 버퍼를 통해 XML 판독기 반환
스트리밍 채널 스택에서 수신 Stream 개체는 XmlReader가 적용된 상태로 네트워크를 통해 들어오는 데이터를 나타냅니다. XmlReader를 사용하여 저장된 WriteNode의 내용을 작성하십시오 저장된 값을 반환합니다. XmlReader
비스트리밍 채널 스택에서 수신 XmlReader로 덮여진 본문 데이터를 포함하는 버퍼 저장된 XmlReader의 내용을 WriteNode를 사용하여 씁니다. 저장된 lang을 반환합니다.

* 이러한 항목들은 Message 서브클래스가 아니라 BodyWriter 클래스의 서브클래스에 직접 구현됩니다. 자세한 내용은 BodyWriter메시지 클래스 사용을 참조하세요.

메시지 헤더

메시지에 헤더가 포함될 수 있습니다. 헤더는 이름, 네임스페이스 및 기타 몇 가지 속성과 연결된 XML Infoset으로 논리적으로 구성됩니다. 메시지 헤더는 .의 Headers 속성을 사용하여 액세스됩니다 Message. 각 헤더는 클래스로 MessageHeader 표시됩니다. 일반적으로 메시지 헤더는 SOAP 메시지와 함께 작동하도록 구성된 채널 스택을 사용할 때 SOAP 메시지 헤더에 매핑됩니다.

메시지 헤더에 정보를 넣고 해당 헤더에서 정보를 추출하는 것은 메시지 본문을 사용하는 것과 유사합니다. 스트리밍이 지원되지 않으므로 프로세스가 다소 간소화되었습니다. 동일한 헤더의 내용에 두 번 이상 액세스할 수 있으며 헤더에 임의 순서로 액세스할 수 있으므로 헤더가 항상 버퍼링됩니다. 헤더를 통해 XML 판독기를 가져오는 데 사용할 수 있는 범용 메커니즘은 MessageHeader 없지만 이러한 기능을 사용하여 읽을 수 있는 헤더를 나타내는 WCF 내부 하위 클래스가 있습니다. 이 형식 MessageHeader 은 사용자 지정 애플리케이션 헤더가 있는 메시지가 들어올 때 채널 스택에 의해 만들어집니다. 이렇게 하면 서비스 프레임워크에서 이러한 헤더를 해석하기 위해 역직렬화 엔진(예: DataContractSerializer역직렬화 엔진)을 사용할 수 있습니다.

자세한 내용은 메시지 클래스 사용을 참조하세요.

메시지 속성

메시지에 속성이 포함될 수 있습니다. 속성은 문자열 이름과 연결된 모든 .NET Framework 개체입니다. 속성은 .의 Properties 속성을 통해 액세스됩니다 Message.

일반적으로 SOAP 본문 및 SOAP 헤더에 매핑되는 메시지 본문 및 메시지 헤더와 달리 메시지 속성은 일반적으로 메시지와 함께 전송되거나 수신되지 않습니다. 메시지 속성은 주로 채널 스택의 다양한 채널 간 및 채널 스택과 서비스 모델 간에 메시지에 대한 데이터를 전달하는 통신 메커니즘으로 존재합니다.

예를 들어 WCF의 일부로 포함된 HTTP 전송 채널은 클라이언트에 회신을 보낼 때 "404(찾을 수 없음)" 및 "500(내부 서버 오류)"과 같은 다양한 HTTP 상태 코드를 생성할 수 있습니다. 회신 메시지를 보내기 전에 PropertiesMessage에 "httpResponse"라는 속성이 있고, 이 속성이 HttpResponseMessageProperty 형식의 객체를 포함하는지 확인합니다. 해당 속성이 발견되면 StatusCode 속성을 확인하고 그 상태 코드를 사용합니다. 찾을 수 없으면 기본 "200(OK)" 코드가 사용됩니다.

자세한 내용은 메시지 클래스 사용을 참조하세요.

전체 메시지

지금까지는 메시지의 다양한 부분에 격리된 상태로 액세스하는 방법에 대해 설명했습니다. 그러나 클래스는 Message 전체 메시지로 작업하는 메서드도 제공합니다. 예를 들어 메서드는 WriteMessage XML 작성기에 전체 메시지를 씁니다.

이렇게 하려면 전체 Message 인스턴스와 XML Infoset 간에 매핑을 정의해야 합니다. 실제로 이러한 매핑이 존재합니다. WCF는 SOAP 표준을 사용하여 이 매핑을 정의합니다. 인스턴스가 Message XML Infoset으로 작성되면 결과 Infoset은 메시지가 포함된 유효한 SOAP 봉투입니다. WriteMessage 따라서 일반적으로 다음 단계를 수행합니다.

  1. SOAP 봉투 요소의 시작 태그를 작성합니다.

  2. SOAP 헤더 요소 여는 태그를 작성하고, 모든 헤더를 쓰고, 헤더 요소를 닫습니다.

  3. SOAP 본문 요소 여는 태그를 작성합니다.

  4. WriteBodyContents 또는 동일한 메서드를 호출하여 본문을 작성하세요.

  5. 본문 및 봉투 요소를 닫습니다.

위의 단계는 SOAP 표준과 밀접하게 연관되어 있습니다. 이는 여러 버전의 SOAP가 존재하기 때문에 복잡합니다. 예를 들어 SOAP 버전을 사용하지 않고는 SOAP 봉투 요소를 올바르게 작성할 수 없습니다. 또한 경우에 따라 이 복잡한 SOAP 관련 매핑을 완전히 해제하는 것이 바람직할 수 있습니다.

이러한 목적을 위해 Version의 속성이 Message에 제공됩니다. 메시지를 작성할 때 사용할 SOAP 버전으로 설정하거나 SOAP별 매핑을 방지하도록 None 설정할 수 있습니다. 속성이 Version 설정된 None경우 전체 메시지와 함께 작동하는 메서드는 메시지가 본문으로만 구성된 것처럼 작동합니다. 예를 들어 WriteMessage 위에 나열된 여러 단계를 수행하는 대신 호출하기만 WriteBodyContents 하면 됩니다. 들어오는 메시지 Version 에서 자동 검색되고 올바르게 설정될 것으로 예상됩니다.

채널 스택

채널

앞서 설명한 것처럼 채널 스택은 나가는 Message 인스턴스를 일부 작업(예: 네트워크를 통해 패킷 보내기)으로 변환하거나 일부 작업(예: 네트워크 패킷 수신)을 들어오는 Message 인스턴스로 변환하는 작업을 담당합니다.

채널 스택은 순서에 따라 하나 이상의 채널로 구성됩니다. 나가는 Message 인스턴스는 스택의 첫 번째 채널( 최상위 채널이라고도 함)에 전달되며, 이 채널은 스택의 다음 채널로 전달됩니다. 메시지는 전송 채널이라고 하는 마지막 채널에서 종료됩니다. 들어오는 메시지는 전송 채널에서 시작되며 채널에서 스택 위로 채널로 전달됩니다. 맨 위 채널에서 메시지는 일반적으로 서비스 프레임워크에 전달됩니다. 이는 애플리케이션 메시지의 일반적인 패턴이지만 일부 채널은 약간 다르게 작동할 수 있습니다. 예를 들어 위의 채널에서 메시지를 전달하지 않고도 자체 인프라 메시지를 보낼 수 있습니다.

채널은 메시지가 스택을 통과하면서 다양한 방법으로 이를 처리할 수 있습니다. 가장 일반적인 작업은 보내는 메시지에 헤더를 추가하고 들어오는 메시지에서 헤더를 읽는 것입니다. 예를 들어 채널은 메시지의 디지털 서명을 계산하고 헤더로 추가할 수 있습니다. 또한 채널은 들어오는 메시지에서 이 디지털 서명 헤더를 검사하고 유효한 서명이 없는 메시지가 채널 스택 위로 올라가지 못하도록 차단할 수도 있습니다. 또한 채널은 종종 메시지 속성을 설정하거나 검사합니다. 메시지 본문은 일반적으로 수정되지 않지만, 허용됩니다. 예를 들어 WCF 보안 채널은 메시지 본문을 암호화할 수 있습니다.

전송 채널 및 메시지 인코더

스택에서 맨 아래 채널은 다른 채널에 의해 수정된 Message을 실제로 어떤 동작으로 변환하는 작업을 담당합니다. 수신 측에서는 일부 작업을 다른 채널들이 처리하는 Message로 변환하는 채널입니다.

앞에서 설명한 것처럼 다양한 프로토콜을 통해 네트워크 패킷을 보내거나 받거나, 데이터베이스에서 메시지를 읽거나 쓰거나, 메시지 큐에서 메시지를 큐에 추가하거나 제거하는 작업이 포함될 수 있습니다. 이러한 모든 작업에는 한 가지 공통점이 있습니다. WCFMessage 인스턴스와 전송, 수신, 읽기, 쓰기, 큐에서 대기 또는 큐에서 제거할 수 있는 실제 바이트 그룹 간에 변환이 필요합니다. 바이트 그룹으로 변환하는 Message 프로세스를 인코딩이라고 하며 바이트 그룹에서 만드는 Message 역방향 프로세스를 디코딩이라고 합니다.

대부분의 전송 채널은 메시지 인코더 라는 구성 요소를 사용하여 인코딩 및 디코딩 작업을 수행합니다. 메시지 인코더는 클래스의 MessageEncoder 하위 클래스입니다. MessageEncoder에는 ReadMessageWriteMessage 메서드 오버로드가 포함되어 있어 Message와 바이트 그룹 간의 변환을 수행할 수 있습니다.

보내는 쪽에서, 버퍼링 전송 채널 Message은 위의 채널에서 받은 개체를 WriteMessage으로 전달합니다. 바이트 배열을 다시 가져오고, 이 바이트를 유효한 TCP 패킷으로 패키징하고 올바른 대상으로 보내는 등의 작업을 수행하는 데 사용합니다. 스트리밍 전송 채널은 먼저 나가는 TCP 연결을 통해 Stream를 생성한 다음, 전송해야 하는 StreamMessage를 적절한 WriteMessage 오버로드에 전달하여 메시지를 작성합니다.

수신 측에서 버퍼링 전송 채널은 들어오는 바이트(예: 들어오는 TCP 패킷에서)를 배열로 추출한 후에, 채널 스택 위로 더 전달할 수 있는 ReadMessage 객체를 얻기 위해 Message을(를) 호출합니다. 스트리밍 전송 채널은 들어오는 TCP 연결을 통해 네트워크 스트림과 같은 Stream 개체를 생성한 후, 이를 ReadMessage에 전달하여 Message 개체를 반환받습니다.

전송 채널과 메시지 인코더 간의 분리는 필수가 아닙니다. 메시지 인코더를 사용하지 않는 전송 채널을 작성할 수 있습니다. 그러나 이러한 분리의 장점은 구성이 용이하다는 것이다. 전송 채널이 기본 MessageEncoder채널만 사용하는 한 모든 WCF 또는 타사 메시지 인코더에서 작동할 수 있습니다. 마찬가지로 일반적으로 모든 전송 채널에서 동일한 인코더를 사용할 수 있습니다.

메시지 인코더 작업

인코더의 일반적인 작업을 설명하기 위해 다음 네 가지 경우를 고려하는 것이 유용합니다.

수술 주석
인코딩, 버퍼링 버퍼링 모드에서 인코더는 일반적으로 변수 크기 버퍼를 만든 다음, 이 버퍼 위에 XML 기록기를 만듭니다. 그런 다음, 이 항목에 대한 이전 섹션에서 WriteMessage(XmlWriter) 설명한 대로 인코딩되는 메시지에 대해 WriteBodyContents(XmlDictionaryWriter)를 호출합니다. 그러면 Message를 사용하여 헤더를 쓴 후 본문을 씁니다. 그런 다음 전송 채널에서 사용할 버퍼의 내용(바이트 배열로 표시됨)이 반환됩니다.
인코딩, 스트리밍 스트리밍 모드에서 작업은 위와 비슷하지만 더 간단합니다. 버퍼가 필요하지 않습니다. XML 작성기는 일반적으로 스트림을 통해 생성되며 WriteMessage(XmlWriter) 에서 호출되어 이 작성기에 기록됩니다.
디코딩, 버퍼링 버퍼링 모드에서 디코딩하는 경우 버퍼링된 데이터를 포함하는 특수 Message 서브클래스가 일반적으로 만들어집니다. 메시지의 헤더를 읽고 메시지 본문에 배치된 XML 판독기를 만듭니다. GetReaderAtBodyContents()와 함께 반환될 리더기입니다.
디코딩, 스트리밍 스트리밍 모드에서 디코딩하는 경우 특수 메시지 하위 클래스가 일반적으로 생성됩니다. 스트림은 모든 헤더를 읽고 메시지 본문에 배치할 만큼 충분히 고급입니다. 그런 다음 스트림을 통해 XML 판독기를 만듭니다. GetReaderAtBodyContents()와 함께 반환될 리더기입니다.

인코더는 다른 함수도 수행할 수 있습니다. 예를 들어 인코더는 XML 판독기와 작성기를 공유할 수 있습니다. 필요할 때마다 새 XML 판독기 또는 작성기를 만드는 데 비용이 많이 듭니다. 따라서 인코더는 일반적으로 판독기 풀과 구성 가능한 크기의 기록기 풀을 유지 관리합니다. 앞에서 설명한 인코더 작업에 대한 설명에서 "XML 판독기/작성기 만들기"라는 문구를 사용할 때마다 일반적으로 "풀에서 하나 가져가거나 사용할 수 없는 경우 새로 만듭니다"를 의미합니다. 인코더(및 Message 디코딩하는 동안 만드는 서브클래스)에는 판독기와 작성기가 더 이상 필요하지 않은 풀(예: 닫힌 경우 Message )으로 반환하는 논리가 포함되어 있습니다.

WCF는 추가 사용자 지정 형식을 만들 수 있지만 세 개의 메시지 인코더를 제공합니다. 제공된 형식은 텍스트, 이진 및 MTOM(메시지 전송 최적화 메커니즘)입니다. 이러한 내용은 메시지 인코더 선택에 자세히 설명되어 있습니다.

IStreamProvider 인터페이스

나가는 메시지를 XML 작성기에서 스트리밍된 본문으로 작성할 때, Message는 구현에서 다음과 같은 호출 시퀀스를 사용합니다.

  • 스트림 앞에 필요한 정보를 작성합니다(예: 여는 XML 태그).

  • 스트림을 기록합니다.

  • 스트림 뒤의 모든 정보(예: 닫는 XML 태그)를 작성합니다.

이는 텍스트 XML 인코딩과 유사한 인코딩에서 잘 작동합니다. 그러나 일부 인코딩은 XML 정보 세트 정보(예: XML 요소 시작 및 종료에 대한 태그)를 요소 내에 포함된 데이터와 함께 배치하지 않습니다. 예를 들어 MTOM 인코딩에서 메시지는 여러 부분으로 분할됩니다. 한 부분에는 실제 요소 내용에 대한 다른 부분에 대한 참조가 포함될 수 있는 XML Infoset이 포함되어 있습니다. XML Infoset은 일반적으로 스트리밍된 콘텐츠에 비해 작으므로 Infoset을 버퍼링하고, 작성한 다음, 스트리밍된 방식으로 콘텐츠를 작성하는 것이 좋습니다. 즉, 닫는 요소 태그를 작성할 때까지 스트림이 아직 작성되지 않았어야 합니다.

이를 위해 인터페이스가 IStreamProvider 사용됩니다. 인터페이스에는 GetStream() 쓸 스트림을 반환하는 메서드가 있습니다. 스트리밍된 메시지 본문을 OnWriteBodyContents(XmlDictionaryWriter) 작성하는 올바른 방법은 다음과 같습니다.

  1. 스트림 앞에 필요한 정보를 작성합니다(예: 여는 XML 태그).

  2. WriteValue의 오버로드를 호출하고, XmlDictionaryWriter을(를) 받는 IStreamProviderIStreamProvider 구현을 적용하여 작성할 스트림을 반환합니다.

  3. 스트림 뒤의 모든 정보(예: 닫는 XML 태그)를 작성합니다.

이 방법을 사용하면 XML 작성기에서 스트리밍된 데이터를 호출 GetStream() 하고 쓸 시기를 선택할 수 있습니다. 예를 들어 텍스트 및 이진 XML 기록기는 즉시 호출하고 시작 태그와 끝 태그 사이에 스트리밍된 콘텐츠를 기록합니다. MTOM 작성기는 메시지의 적절한 부분을 작성할 준비가 되면 나중에 호출 GetStream() 하도록 결정할 수 있습니다.

Service Framework에서 데이터 표현

이 항목의 "기본 아키텍처" 섹션에 설명된 것처럼 서비스 프레임워크는 WCF의 일부로, 무엇보다도 메시지 데이터와 실제 Message 인스턴스에 대한 사용자 친화적인 프로그래밍 모델 간 변환을 담당합니다. 일반적으로 메시지 교환은 서비스 프레임워크에서 OperationContractAttribute 특성으로 표시된 .NET Framework 메서드로 표현됩니다. 메서드는 일부 매개 변수를 사용할 수 있으며 반환 값 또는 출력 매개 변수(또는 둘 다)를 반환할 수 있습니다. 서비스 쪽에서 입력 매개 변수는 들어오는 메시지를 나타내고 반환 값과 출력 매개 변수는 나가는 메시지를 나타냅니다. 클라이언트 쪽에서는 반대의 경우도 마찬가지입니다. 매개 변수 및 반환 값을 사용하여 메시지를 설명하는 프로그래밍 모델은 서비스 계약에서 데이터 전송 지정에 자세히 설명되어 있습니다. 그러나 이 섹션에서는 간략한 개요를 제공합니다.

프로그래밍 모델

WCF 서비스 프레임워크는 메시지를 설명하기 위한 5가지 프로그래밍 모델을 지원합니다.

1. 빈 메시지

이것이 가장 간단한 경우입니다. 들어오는 빈 메시지를 설명하려면 입력 매개 변수를 사용하지 마세요.

[OperationContract]
int GetCurrentTemperature();
<OperationContract()> Function GetCurrentTemperature() As Integer

빈 나가는 메시지를 설명하려면 void 반환 값을 사용하고 out 매개 변수를 사용하지 않습니다.

[OperationContract]
void SetDesiredTemperature(int t);
<OperationContract()> Sub SetDesiredTemperature(ByVal t As Integer)

단방향 작업 계약과는 다릅니다.

[OperationContract(IsOneWay = true)]
void SetLightbulb(bool isOn);
<OperationContract(IsOneWay:=True)> Sub SetLightbulb(ByVal isOn As Boolean)

SetDesiredTemperature 이 예제에서는 양방향 메시지 교환 패턴에 대해 설명합니다. 작업에서 메시지가 반환되지만 비어 있습니다. 연산에서 오류가 반환될 수 있습니다. "전구 설정" 예제에서 메시지 교환 패턴은 단방향이므로 설명할 나가는 메시지가 없습니다. 이 경우 서비스는 어떤 상태도 클라이언트에 다시 전달할 수 없습니다.

2. 메시지 클래스 직접 사용

작업 계약에서 Message 클래스(또는 해당 서브클래스 중 하나)를 직접 사용할 수 있습니다. 이 경우, 서비스 프레임워크가 추가 처리 없이 Message을(를) 작업에서 채널 스택으로 전달하고, 그 반대로도 전달합니다.

직접 사용하기 위한 두 가지 주요 사용 Message 사례가 있습니다. 다른 프로그래밍 모델 중 어느 것도 메시지를 설명할 수 있는 충분한 유연성을 제공하지 않는 고급 시나리오에 사용할 수 있습니다. 예를 들어 파일의 속성이 메시지 헤더가 되고 파일의 내용이 메시지 본문이 되는 디스크의 파일을 사용하여 메시지를 설명할 수 있습니다. 그런 다음 다음과 유사한 항목을 만들 수 있습니다.

public class FileMessage : Message
// Code not shown.
Public Class FileMessage
    Inherits Message
    ' Code not shown.

작업 계약에서 두 번째로 일반적으로 사용하는 Message 것은 서비스가 특정 메시지 내용에 대해 신경 쓰지 않고 블랙박스에서와 같이 메시지에서 동작하는 경우입니다. 예를 들어 다른 여러 받는 사람에게 메시지를 전달하는 서비스가 있을 수 있습니다. 계약은 다음과 같이 작성할 수 있습니다.

[OperationContract]
public FileMessage GetFile()
{
    //code omitted…
    FileMessage fm = new FileMessage("myFile.xml");
    return fm;
}
<OperationContract()> Public Function GetFile() As FileMessage
    'code omitted…
    Dim fm As New FileMessage("myFile.xml")
    Return fm
End Function

Action="*" 줄은 메시지 디스패치를 효과적으로 비활성화하고 모든 메시지가 IForwardingService 계약으로 전송되어 ForwardMessage 작업으로 도달하게 합니다. 일반적으로 디스패처는 메시지의 "작업" 헤더를 검사하여 의도한 작업을 결정합니다. Action="*"은 "Action 헤더의 가능한 모든 값"을 의미합니다.) Action="*" 및 Message를 매개 변수로 사용하는 조합을 "유니버설 계약"으로 알려져 있습니다. 가능한 모든 메시지를 받을 수 있기 때문입니다. 가능한 모든 메시지를 보내려면 메시지를 반환 값으로 사용하고 "*"로 설정합니다 ReplyAction . 이렇게 하면 서비스 프레임워크가 고유한 작업 헤더를 추가하지 못하게 되므로 반환하는 개체를 사용하여 이 헤더를 Message 제어할 수 있습니다.

3. 메시지 계약

WCF는 메시지 계약이라는 메시지를 설명하기 위한 선언적 프로그래밍 모델을 제공합니다. 이 모델은 메시지 계약 사용에 대해 자세히 설명합니다. 기본적으로 전체 메시지는 메시지 계약 클래스의 어떤 부분이 메시지의 어느 부분에 매핑되어야 하는지와 같은 MessageBodyMemberAttributeMessageHeaderAttribute 특성을 사용하는 단일 .NET Framework 형식으로 표시됩니다.

메시지 계약은 결과 Message 인스턴스에 대한 많은 제어를 제공합니다(하지만 Message 클래스를 직접 사용하는 것보다는 적은 제어를 할 수 있습니다). 예를 들어 메시지 본문은 각각 자체 XML 요소로 표현되는 여러 정보로 구성되는 경우가 많습니다. 이러한 요소는 본문에서 직접 발생하거나(있는 그대로 모드) 포함 XML 요소에 포장된 상태로 나타날 수 있습니다. 메시지 계약 프로그래밍 모델을 사용하면 일반 메시지 형식과 래핑된 메시지 형식 중에서 선택할 수 있으며, 래퍼 이름과 네임스페이스를 제어할 수 있습니다.

메시지 계약의 다음 코드 예제에서는 이러한 기능을 보여 줍니다.

[MessageContract(IsWrapped = true, WrapperName = "Order")]
public class SubmitOrderMessage
{
    [MessageHeader]
    public string customerID;
    [MessageBodyMember]
    public string item;
    [MessageBodyMember]
    public int quantity;
}
<MessageContract(IsWrapped:=True, WrapperName:="Order")> _
Public Class SubmitOrderMessage
    <MessageHeader()> Public customerID As String
    <MessageBodyMember()> Public item As String
    <MessageBodyMember()> Public quantity As Integer
End Class

직렬화되도록 표시된 항목( MessageBodyMemberAttribute, MessageHeaderAttribute또는 기타 관련 특성 포함)은 메시지 계약에 참여하려면 직렬화할 수 있어야 합니다. 자세한 내용은 이 항목의 뒷부분에 있는 "Serialization" 섹션을 참조하세요.

4. 매개 변수

종종 여러 데이터 조각에서 작동하는 작업을 설명하려는 개발자는 메시지 계약에서 제공하는 제어 수준을 필요로 하지 않습니다. 예를 들어 새 서비스를 만들 때는 일반적으로 래핑할지 여부와 래퍼 요소 이름을 결정하려고 하지 않습니다. 이러한 결정을 내리려면 웹 서비스 및 SOAP에 대한 깊은 지식이 필요한 경우가 많습니다.

WCF 서비스 프레임워크는 사용자에게 이러한 선택을 강제하지 않고 여러 관련 정보를 보내거나 받기 위한 최상의 상호 운용 가능한 SOAP 표현을 자동으로 선택할 수 있습니다. 이 작업은 이러한 정보를 작업 계약의 매개 변수 또는 반환 값으로 간단히 설명하여 수행됩니다. 예를 들어 다음 작업 계약을 고려합니다.

[OperationContract]
void SubmitOrder(string customerID, string item, int quantity);
<OperationContract()> _
Sub SubmitOrder( _
    ByVal customerID As String, _
    ByVal item As String, _
    ByVal quantity As Integer)

서비스 프레임워크는 세 가지 정보(customerIDitemquantity)를 모두 메시지 본문에 넣고 래퍼 SubmitOrderRequest요소에 래핑하도록 자동으로 결정합니다.

더 복잡한 메시지 계약 또는 기반 프로그래밍 모델로 이동하는 특별한 이유가 없는 한, 작업 계약 매개 변수의 간단한 목록으로 보내거나 Message받을 정보를 설명하는 것이 좋습니다.

5. 스트림

작업 계약에서 또는 메시지 계약의 유일한 메시지 본문 부분으로 해당 서브클래스 중 하나를 사용하는 Stream 것은 위에서 설명한 것과는 별도의 프로그래밍 모델로 간주될 수 있습니다. 이러한 방식으로 사용하는 Stream 것은 자신의 스트리밍 호환 Message 서브클래스를 작성하지 않고도 계약을 스트리밍 방식으로 사용할 수 있도록 보장하는 유일한 방법입니다. 자세한 내용은 큰 데이터 및 스트리밍을 참조하세요.

Stream 또는 그 서브클래스 중 하나를 이러한 방식으로 사용하는 경우, serializer가 호출되지 않습니다. 보내는 메시지의 경우, 특수 스트리밍 Message 하위 클래스가 생성되고, 인터페이스의 IStreamProvider 섹션에 설명된 대로 스트림이 기록됩니다. 들어오는 메시지의 경우, 서비스 프레임워크는 그 메시지를 기반으로 Stream 서브클래스를 생성하여 그 작업에 제공합니다.

프로그래밍 모델 제한 사항

위에서 설명한 프로그래밍 모델은 임의로 결합할 수 없습니다. 예를 들어 작업에서 메시지 계약 유형을 수락하는 경우 메시지 계약은 유일한 입력 매개 변수여야 합니다. 또한 작업은 빈 메시지(void의 반환 형식) 또는 다른 메시지 계약을 반환해야 합니다. 이러한 프로그래밍 모델 제한 사항은 메시지 계약 사용, 메시지클래스 사용, 대용량 데이터 및 스트리밍 등 각 특정 프로그래밍 모델에 대한 항목에 설명되어 있습니다.

메시지 포맷터

위에서 설명한 프로그래밍 모델은 메시지 포맷터 라는 구성 요소를 서비스 프레임워크에 연결하여 지원됩니다. 메시지 포맷터는 각각 클라이언트와 서비스 WCF 클라이언트에서 사용하기 위해 IClientMessageFormatter 또는 IDispatchMessageFormatter 인터페이스를 구현하는 형식입니다.

메시지 포맷터는 일반적으로 동작에 의해 연결됩니다. 예를 들어 DataContractSerializerOperationBehavior 데이터 계약 메시지 포맷터에 통합됩니다. 이 작업은 서비스 측에서는 Formatter를 메서드 ApplyDispatchBehavior(OperationDescription, DispatchOperation)에서 올바른 포맷터로 설정하여 수행하거나, 클라이언트 측에서는 메서드 Formatter에서 ApplyClientBehavior(OperationDescription, ClientOperation)를 올바른 포맷터로 설정하여 수행됩니다.

다음 표에서는 메시지 포맷터가 구현할 수 있는 메서드를 나열합니다.

인터페이스 메서드 조치
IDispatchMessageFormatter DeserializeRequest(Message, Object[]) 들어오는 Message 매개 변수를 작업 매개 변수로 변환합니다.
IDispatchMessageFormatter SerializeReply(MessageVersion, Object[], Object) 작업 반환 값/출력 매개 변수에서 나가는 Message 매개 변수를 만듭니다.
IClientMessageFormatter SerializeRequest(MessageVersion, Object[]) 작업 매개 변수에서 나가는 Message 매개 변수를 만듭니다.
IClientMessageFormatter DeserializeReply(Message, Object[]) 들어오는 Message 매개 변수를 반환 값/출력 매개 변수로 변환합니다.

직렬화

메시지 계약 또는 매개 변수를 사용하여 메시지 내용을 설명할 때마다 serialization을 사용하여 .NET Framework 형식과 XML Infoset 표현 간에 변환해야 합니다. 직렬화는 WCF의 다른 위치에서 사용됩니다. 예를 들어 Message 개체로 역직렬화된 메시지의 전체 본문을 읽는 데 사용할 수 있는 제네릭 GetBody 메서드가 있습니다.

WCF는 매개 변수 및 메시지 파트를 직렬화 및 역직렬화하기 위한 "기본 제공" 두 가지 직렬화 기술, DataContractSerializerXmlSerializer를 지원합니다. 또한 사용자 지정 serializer를 작성할 수 있습니다. 그러나 WCF의 다른 부분(예: 제네릭 GetBody 메서드 또는 SOAP 오류 직렬화)은 XmlObjectSerializer 서브클래스(DataContractSerializerNetDataContractSerializer, 단 XmlSerializer는 아님)만 사용하도록 제한될 수 있으며, 또는 DataContractSerializer만 사용하도록 하드 코딩되어 있을 수 있습니다.

ASP.NET 웹 서비스에 사용되는 직렬화 엔진입니다. 새 DataContractSerializer 데이터 계약 프로그래밍 모델을 이해하는 새 직렬화 엔진입니다. DataContractSerializer 는 기본 옵션이며, XmlSerializer 사용 여부는 DataContractFormatAttribute 속성을 사용하여 각 작업별로 선택할 수 있습니다.

DataContractSerializerOperationBehaviorXmlSerializerOperationBehavior은 각각 DataContractSerializerXmlSerializer에 대한 메시지 포맷터를 연결하는 작업 동작을 담당합니다. DataContractSerializerOperationBehavior 동작은 XmlObjectSerializer에서 파생되는 모든 직렬 변환기와 함께 실제로 작동할 수 있으며, NetDataContractSerializer도 포함됩니다(자세한 내용은 Stand-Alone 직렬화 사용을 참조). 이 동작은 가상 메서드 오버로드 중 CreateSerializer 하나를 호출하여 serializer를 가져옵니다. 다른 serializer를 사용하려면 새 DataContractSerializerOperationBehavior 하위 클래스를 만들고 두 CreateSerializer 오버로드를 모두 재정의합니다.

참고하십시오