Compartir a través de


Procedimiento para habilitar el streaming

Windows Communication Foundation (WCF) puede enviar mensajes mediante transferencias almacenadas en búfer o por secuencias. En el modo de transferencia almacenado en búfer (predeterminado), se debe entregar completamente un mensaje antes de que un receptor pueda leerlo. En modo de transferencia de transmisión por secuencias, el receptor puede empezar a procesar el mensaje antes de se entregue completamente. El modo de transmisión por secuencias es útil cuando la información que se pasa es larga y puede procesarse en serie. El modo de transmisión por secuencias también es útil cuando el mensaje es demasiado grande para que se almacene en búfer completamente.

Para habilitar la transmisión por secuencias, defina apropiadamente OperationContract y habilite la transmisión por secuencias en el nivel de transporte.

Transmisión de datos por secuencias

  1. Para transmitir datos por secuencias, OperationContract del servicio debe satisfacer dos requisitos:

    1. El parámetro que contiene los datos que se van a transmitir debe ser el único parámetro del método. Por ejemplo, si el mensaje de entrada es el que se va a transmitir por secuencia, la operación debe tener exactamente un parámetro de entrada. De igual forma, si el mensaje de salida se va a transmitir por secuencia, la operación debe tener exactamente un parámetro de salida o un valor devuelto.

    2. Al menos uno de los tipos del parámetro y el valor devuelto debería ser Stream, Messageo IXmlSerializable.

    A continuación se muestra un ejemplo de los datos.

    [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
    

    La operación GetStream recibe algunos datos de entrada en búfer como string, que también se almacena en búfer y devuelve Stream, que se transmite por secuencias. A la inversa, UploadStream admite una Stream (transmitida por secuencias) y devuelve un bool (almacenado en búfer). EchoStream toma y devuelve Stream y es un ejemplo de una operación cuyos mensajes de entrada y salida se transmiten por secuencias. Finalmente, GetReversedStream no toma ninguna entrada y devuelve un Stream (transmitido por secuencia).

  2. La transmisión por secuencias debe habilitarse en el enlace. Defina una propiedad TransferMode, que puede adoptar uno de los siguientes valores:

    1. Buffered,

    2. Streamed, que habilita la comunicación mediante transmisión por secuencias en ambas direcciones.

    3. StreamedRequest, que solo habilita la solicitud de transmisión.

    4. StreamedResponse, que solo habilita la transmisión por secuencias de la respuesta.

    BasicHttpBinding expone la propiedad TransferMode en el enlace, tal y como hace, NetTcpBinding y NetNamedPipeBinding. La propiedad TransferMode se puede establecer también en el elemento de enlace del transporte y utilizarse en un enlace personalizado.

    Los siguientes ejemplos muestran cómo establecer TransferMode mediante código y cambiando el archivo de configuración. Ambos ejemplos también establecen la propiedad maxReceivedMessageSize en 64 MB, que coloca un límite en el tamaño máximo permitido de mensajes que se reciben. El maxReceivedMessageSize predeterminado es de 64 KB, que normalmente es demasiado pequeño para los escenarios de transmisión por secuencias. Establezca este valor de cuota que depende, según corresponda, del tamaño máximo de mensajes que su aplicación espera recibir. También tenga en cuenta que maxBufferSize controla el tamaño máximo que se almacena en búfer y lo establece de manera apropiada.

    1. El siguiente fragmento de código de configuración del ejemplo muestra cómo establecer la propiedad TransferMode en la transmisión por secuencias en basicHttpBinding y un enlace HTTP personalizado.

      <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. El siguiente fragmento de código muestra cómo establecer la propiedad TransferMode para la transmisión por secuencias en basicHttpBinding y un enlace HTTP personalizado.

      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. El siguiente fragmento de código muestra cómo establecer la propiedad TransferMode para la transmisión por secuencias en un enlace HTTP personalizado.

      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. Las operaciones GetStream, UploadStream y EchoStream tratan con el envío de datos directamente desde un archivo o guardando los datos recibidos directamente en un archivo. El siguiente código se aplica a 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
    

Escritura de una secuencia personalizada

  1. Para hacer un procesamiento especial en cada fragmento de un flujo de datos mientras se envía o recibe, derive una clase de flujo personalizada de Stream. Como un ejemplo de una secuencia personalizada, el siguiente código contiene un método GetReversedStream y una clase ReverseStream.

    GetReversedStream crea y devuelve una nueva instancia de ReverseStream. El procesamiento real se produce cuando el sistema lee desde el objeto ReverseStream. El método ReverseStream.Read lee un fragmento de bytes del archivo subyacente, los invierte y después devuelve los bytes invertidos. Este método no invierte el contenido del archivo completo, sino un fragmento de bytes cada vez. Este ejemplo muestra cómo puede realizar el procesamiento de la secuencia cuando el contenido se lee o escribe desde la secuencia.

    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
    

Consulte también