Cómo habilitar la transmisión
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
Para transmitir datos por secuencias, OperationContract del servicio debe satisfacer dos requisitos:
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.
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()> _ 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
[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(); }
La operación
GetStream
recibe algunos datos de entrada en búfer comostring
, que también se almacena en búfer y devuelveStream
, que se transmite por secuencias. A la inversa,UploadStream
toma en unStream
(transmitido por secuencia) y devuelve unbool
(almacenado en búfer).EchoStream
toma y devuelveStream
y es un ejemplo de una operación cuyos mensajes de entrada y salida se transmiten por secuencia. Finalmente,GetReversedStream
no toma ninguna entrada y devuelve unStream
(transmitido por secuencia).La transmisión por secuencias debe habilitarse en el enlace. Defina una propiedad TransferMode, que puede adoptar uno de los siguientes valores:
Buffered,
Streamed, que habilita la comunicación mediante transmisión por secuencias en ambas direcciones.
StreamedRequest, que solo habilita la solicitud de transmisión.
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. ElmaxReceivedMessageSize
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.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>
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 Shared Function CreateStreamingBinding() As Binding Dim b As New BasicHttpBinding() b.TransferMode = TransferMode.Streamed Return b End Function
public static Binding CreateStreamingBinding() { BasicHttpBinding b = new BasicHttpBinding(); b.TransferMode = TransferMode.Streamed; return b; }
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 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
public static Binding CreateStreamingBinding() { TcpTransportBindingElement transport = new TcpTransportBindingElement(); transport.TransferMode = TransferMode.Streamed; BinaryMessageEncodingBindingElement encoder = new BinaryMessageEncodingBindingElement(); CustomBinding binding = new CustomBinding(encoder, transport); return binding; }
Las operaciones
GetStream
,UploadStream
yEchoStream
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 aGetStream
.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
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; } }
Escritura de una secuencia personalizada
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 claseReverseStream
.GetReversedStream
crea y devuelve una nueva instancia deReverseStream
. El procesamiento real se produce cuando el sistema lee desde el objetoReverseStream
. El métodoReverseStream.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.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
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; } } }