Share via


방법: 스트리밍 사용

WCF(Windows Communication Foundation)에서는 버퍼링 전송 또는 스트리밍 전송을 사용하여 메시지를 보낼 수 있습니다. 기본 설정인 버퍼링된 전송 모드에서는 메시지가 완전히 전달되어야 수신자가 읽을 수 있습니다. 스트리밍 전송 모드에서는 메시지가 완전히 전달되기 전에 수신자가 메시지 처리를 시작할 수 있습니다. 전달되는 정보가 길고 순차적으로 처리 가능한 경우 스트리밍 모드가 유용합니다. 또한 전체를 버퍼링하기에는 메시지가 너무 큰 경우에도 스트리밍 모드가 효과적입니다.

스트리밍을 사용하려면 OperationContract를 적절히 정의하고 전송 수준에서 스트리밍을 사용합니다.

데이터를 스트리밍하려면

  1. 데이터를 스트리밍하려면 서비스에 대한 OperationContract가 두 가지 요구 사항을 충족해야 합니다.

    1. 전송할 데이터를 보유하는 매개 변수가 메서드의 유일한 매개 변수이어야 합니다. 예를 들어, 입력 메시지를 스트리밍할 경우 해당 작업은 단 하나의 입력 매개 변수만 가져야 합니다. 마찬가지로, 출력 메시지를 스트리밍할 경우 해당 작업에는 출력 매개 변수 또는 반환 값이 하나만 있어야 합니다.

    2. 반환 값과 매개 변수 유형 중 하나 이상이 Stream, Message 또는 IXmlSerializable이어야 합니다.

    다음은 스트리밍된 데이터의 계약 예제입니다.

    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
    public interface IStreamingSample
    {
        [OperationContract]
        Stream GetStream(string data);
        [OperationContract]
        bool UploadStream(Stream stream);
        [OperationContract]
        Stream EchoStream(Stream stream);
        [OperationContract]
        Stream GetReversedStream();
    }
    
    <ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
    Public Interface IStreamingSample
        <OperationContract()> _
        Function GetStream(ByVal data As String) As Stream
        <OperationContract()> _
        Function UploadStream(ByVal stream As Stream) As Boolean
        <OperationContract()> _
        Function EchoStream(ByVal stream As Stream) As Stream
        <OperationContract()> _
        Function GetReversedStream() As Stream
    
    End Interface
    

    GetStream 작업은 버퍼링되는 string으로 버퍼링된 입력 데이터를 받고, 스트리밍되는 Stream을 반환합니다. 반대로 UploadStream은 스트리밍된 Stream을 받아 버퍼링된 bool을 반환합니다. EchoStreamStream을 받고 반환하므로 입출력 메시지 모두 스트리밍되는 작업의 예입니다. 마지막으로, GetReversedStream은 어떤 입력도 받지 않고 스트리밍되는 Stream을 반환합니다.

  2. 바인딩에서 스트리밍을 사용하도록 설정해야 합니다. 다음 값 중 하나를 가져올 수 있는 TransferMode 속성을 설정합니다.

    1. Buffered,

    2. Streamed. 양 방향으로 스트리밍 통신이 가능합니다.

    3. StreamedRequest. 요청만 스트리밍이 가능합니다.

    4. StreamedResponse. 응답만 스트리밍이 가능합니다.

    BasicHttpBindingTransferModeNetTcpBinding과 같이 바인딩에 NetNamedPipeBinding 속성을 노출합니다. TransferMode 속성은 전송 바인딩 요소에서 설정하고 사용자 지정 바인딩에서 사용할 수도 있습니다.

    다음 샘플에서는 코드를 사용하거나 구성 파일을 변경하여 TransferMode를 설정하는 방법에 대해 보여 줍니다. 또한 이 샘플에서는 두 방법 모두 maxReceivedMessageSize 속성을 64MB로 설정하여 받을 수 있는 최대 메시지 크기를 설정합니다. 기본 maxReceivedMessageSize는 64KB이며, 이는 스트리밍 시나리오에서는 일반적으로 너무 낮은 수준입니다. 이 할당량을 애플리케이션이 받을 최대 메시지 크기에 따라 적절히 설정합니다. 또한 maxBufferSize는 버퍼링되는 최대 크기를 제어하고 이를 적절히 설정합니다.

    1. 샘플 중 다음 구성 조각은 TransferMode 및 사용자 지정 HTTP 바인딩에서 basicHttpBinding 속성을 스트리밍으로 설정하는 방법에 대해 보여 줍니다.

      <basicHttpBinding>
        <binding name="HttpStreaming" maxReceivedMessageSize="67108864"
                 transferMode="Streamed"/>
      </basicHttpBinding>
      <!-- an example customBinding using Http and streaming-->
      <customBinding>
        <binding name="Soap12">
          <textMessageEncoding messageVersion="Soap12WSAddressing10" />
          <httpTransport transferMode="Streamed" maxReceivedMessageSize="67108864"/>
        </binding>
      </customBinding>
      
    2. 다음 코드 조각은 TransferMode 및 사용자 지정 HTTP 바인딩에서 basicHttpBinding 속성을 스트리밍으로 설정하는 방법에 대해 보여 줍니다.

      public static Binding CreateStreamingBinding()
      {
          BasicHttpBinding b = new BasicHttpBinding();
          b.TransferMode = TransferMode.Streamed;
          return b;
      }
      
      Public Shared Function CreateStreamingBinding() As Binding
          Dim b As New BasicHttpBinding()
          b.TransferMode = TransferMode.Streamed
          Return b
      End Function
      
    3. 다음 코드 조각은 사용자 지정 HTTP 바인딩에서 TransferMode 속성을 스트리밍으로 설정하는 것 방법에 대해 보여 줍니다.

      public static Binding CreateStreamingBinding()
      {
          TcpTransportBindingElement transport = new TcpTransportBindingElement();
          transport.TransferMode = TransferMode.Streamed;
          BinaryMessageEncodingBindingElement encoder = new BinaryMessageEncodingBindingElement();
          CustomBinding binding = new CustomBinding(encoder, transport);
          return binding;
      }
      
      Public Shared Function CreateStreamingBinding() As Binding
          Dim transport As New TcpTransportBindingElement()
          transport.TransferMode = TransferMode.Streamed
          Dim binding As New CustomBinding(New BinaryMessageEncodingBindingElement(), _
                                           transport)
          Return binding
      End Function
      
  3. GetStream, UploadStreamEchoStream 작업 모두 파일로부터 데이터를 직접 보내거나 받은 데이터를 파일에 바로 저장합니다. 다음 코드는 GetStream에 대한 것입니다.

    public Stream GetStream(string data)
    {
        //this file path assumes the image is in
        // the Service folder and the service is executing
        // in service/bin
        string filePath = Path.Combine(
            System.Environment.CurrentDirectory,
            ".\\..\\image.jpg");
        //open the file, this could throw an exception
        //(e.g. if the file is not found)
        //having includeExceptionDetailInFaults="True" in config
        // would cause this exception to be returned to the client
        try
        {
            FileStream imageFile = File.OpenRead(filePath);
            return imageFile;
        }
        catch (IOException ex)
        {
            Console.WriteLine(
                String.Format("An exception was thrown while trying to open file {0}", filePath));
            Console.WriteLine("Exception is: ");
            Console.WriteLine(ex.ToString());
            throw ex;
        }
    }
    
    Public Function GetStream(ByVal data As String) As Stream Implements IStreamingSample.GetStream
        'this file path assumes the image is in
        ' the Service folder and the service is executing
        ' in service/bin 
        Dim filePath = Path.Combine(System.Environment.CurrentDirectory, ".\..\image.jpg")
        'open the file, this could throw an exception 
        '(e.g. if the file is not found)
        'having includeExceptionDetailInFaults="True" in config 
        ' would cause this exception to be returned to the client
        Try
            Return File.OpenRead(filePath)
        Catch ex As IOException
            Console.WriteLine(String.Format("An exception was thrown while trying to open file {0}", filePath))
            Console.WriteLine("Exception is: ")
            Console.WriteLine(ex.ToString())
            Throw ex
        End Try
    End Function
    

사용자 지정 스트림 쓰기

  1. 데이터 스트림의 각 청크에서 데이터를 보내고 받을 때 특수한 처리를 수행하려면 Stream에서 사용자 지정 스트림 클래스를 파생시킵니다. 사용자 지정 스트림의 예로 다음 코드에 GetReversedStream 메서드와 ReverseStream 클래스가 포함되어 있습니다.

    GetReversedStreamReverseStream의 새 인스턴스를 만들어 반환합니다. 시스템이 ReverseStream 개체에서 읽을 때 실제 처리가 이루어집니다. ReverseStream.Read 메서드는 기본 파일에서 바이트 청크를 읽고 이를 되돌린 다음, 되돌린 해당 바이트를 반환합니다. 이 메서드는 파일의 전체 내용을 되돌리지 않으며, 한 번에 하나의 바이트 청크를 되돌립니다. 이는 스트림에서 콘텐츠를 읽거나 쓰는 중에 스트림 처리를 수행하는 방법에 대해 보여 주는 예제입니다.

    class ReverseStream : Stream
    {
    
        FileStream inStream;
        internal ReverseStream(string filePath)
        {
            //opens the file and places a StreamReader around it
            inStream = File.OpenRead(filePath);
        }
        public override bool CanRead
        {
            get { return inStream.CanRead; }
        }
    
        public override bool CanSeek
        {
            get { return false; }
        }
    
        public override bool CanWrite
        {
            get { return false; }
        }
    
        public override void Flush()
        {
            throw new Exception("This stream does not support writing.");
        }
    
        public override long Length
        {
            get { throw new Exception("This stream does not support the Length property."); }
        }
    
        public override long Position
        {
            get
            {
                return inStream.Position;
            }
            set
            {
                throw new Exception("This stream does not support setting the Position property.");
            }
        }
    
        public override int Read(byte[] buffer, int offset, int count)
        {
            int countRead = inStream.Read(buffer, offset, count);
            ReverseBuffer(buffer, offset, countRead);
            return countRead;
        }
    
        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new Exception("This stream does not support seeking.");
        }
    
        public override void SetLength(long value)
        {
            throw new Exception("This stream does not support setting the Length.");
        }
    
        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new Exception("This stream does not support writing.");
        }
        public override void Close()
        {
            inStream.Close();
            base.Close();
        }
        protected override void Dispose(bool disposing)
        {
            inStream.Dispose();
            base.Dispose(disposing);
        }
        void ReverseBuffer(byte[] buffer, int offset, int count)
        {
    
            int i, j;
    
            for (i = offset, j = offset + count - 1; i < j; i++, j--)
            {
                byte currenti = buffer[i];
                buffer[i] = buffer[j];
                buffer[j] = currenti;
            }
        }
    }
    
    Friend Class ReverseStream
        Inherits Stream
    
        Private inStream As FileStream
    
        Friend Sub New(ByVal filePath As String)
            'opens the file and places a StreamReader around it
            inStream = File.OpenRead(filePath)
        End Sub
    
        Public Overrides ReadOnly Property CanRead() As Boolean
            Get
                Return inStream.CanRead
            End Get
        End Property
    
        Public Overrides ReadOnly Property CanSeek() As Boolean
            Get
                Return False
            End Get
        End Property
    
        Public Overrides ReadOnly Property CanWrite() As Boolean
            Get
                Return False
            End Get
        End Property
    
        Public Overrides Sub Flush()
            Throw New Exception("This stream does not support writing.")
        End Sub
    
        Public Overrides ReadOnly Property Length() As Long
            Get
                Throw New Exception("This stream does not support the Length property.")
            End Get
        End Property
    
        Public Overrides Property Position() As Long
            Get
                Return inStream.Position
            End Get
            Set(ByVal value As Long)
                Throw New Exception("This stream does not support setting the Position property.")
            End Set
        End Property
    
        Public Overrides Function Read(ByVal buffer() As Byte, _
                                       ByVal offset As Integer, _
                                       ByVal count As Integer) As Integer
    
            Dim countRead = inStream.Read(buffer, _
                                          offset, _
                                          count)
            ReverseBuffer(buffer, _
                          offset, _
                          countRead)
            Return countRead
        End Function
    
        Public Overrides Function Seek(ByVal offset As Long, _
                                       ByVal origin As SeekOrigin) As Long
            Throw New Exception("This stream does not support seeking.")
        End Function
    
        Public Overrides Sub SetLength(ByVal value As Long)
            Throw New Exception("This stream does not support setting the Length.")
        End Sub
    
        Public Overrides Sub Write(ByVal buffer() As Byte, _
                                   ByVal offset As Integer, _
                                   ByVal count As Integer)
            Throw New Exception("This stream does not support writing.")
        End Sub
    
        Public Overrides Sub Close()
            inStream.Close()
            MyBase.Close()
        End Sub
    
        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            inStream.Dispose()
            MyBase.Dispose(disposing)
        End Sub
    
        Private Sub ReverseBuffer(ByVal buffer() As Byte, _
                                  ByVal offset As Integer, _
                                  ByVal count As Integer)
    
            Dim i = offset
            Dim j = offset + count - 1
    
            Do While i < j
                Dim currenti = buffer(i)
                buffer(i) = buffer(j)
                buffer(j) = currenti
                i += 1
                j -= 1
            Loop
    
        End Sub
    End Class
    

참고 항목