2010-04-28 13 views
12

¿Es correcto que en WCF, no puedo tener un servicio escriba en una secuencia que recibe el cliente?WCF y solicitudes y respuestas de transmisión

Streaming es compatible con WCF para solicitudes, respuestas o ambos. Me gustaría respaldar un escenario en el que el generador de datos (ya sea el cliente en caso de solicitud transmitida o el servidor en caso de transmisión de la respuesta) pueda Escribir en la transmisión. ¿Esto es compatible?

La analogía es Response.OutputStream de una solicitud de ASP.NET. En ASPNET, cualquier página puede invocar a Write en la secuencia de salida y el cliente recibe el contenido. ¿Puedo hacer algo similar en un servicio WCF? ¿Invocar Write en una transmisión que recibe el cliente?

Permítanme explicarlo con una ilustración de WCF. El ejemplo más simple de Streaming en WCF es el servicio que devuelve un FileStream a un cliente. Esta es una respuesta transmitida. El código de servidor para implementar esto, es así:

[ServiceContract] 
public interface IStreamService 
{ 
    [OperationContract] 
    Stream GetData(string fileName); 
} 
public class StreamService : IStreamService 
{ 
    public Stream GetData(string filename) 
    { 
     return new FileStream(filename, FileMode.Open) 
    } 
} 

Y el código de cliente es la siguiente:

StreamDemo.StreamServiceClient client = 
    new WcfStreamDemoClient.StreamDemo.StreamServiceClient(); 
Stream str = client.GetData(@"c:\path\on\server\myfile.dat"); 
do { 
    b = str.ReadByte(); //read next byte from stream 
    ... 
} while (b != -1); 

(ejemplo tomado de http://blog.joachim.at/?p=33)

Claro, ¿verdad? El servidor devuelve Stream al cliente y el cliente invoca Read on it.

¿Es posible que el cliente proporcione un Stream y el servidor para invocar Write en él?
En otras palabras, en lugar de un modelo de extracción, donde el cliente extrae datos del servidor, se trata de un modelo push, donde el cliente proporciona la secuencia "sumidero" y el servidor escribe en ella. El código del lado del servidor puede tener un aspecto similar a este:

[ServiceContract] 
public interface IStreamWriterService 
{ 
    [OperationContract] 
    void SendData(Stream clientProvidedWritableStream); 
} 
public class DataService : IStreamWriterService 
{ 
    public void GetData(Stream s) 
    { 
     do { 
      byte[] chunk = GetNextChunk(); 
      s.Write(chunk,0, chunk.Length); 
     } while (chunk.Length > 0); 
    } 
} 

Es esto posible en WCF, y si es así, ¿cómo? ¿Cuáles son los ajustes de configuración necesarios para el enlace, la interfaz, etc.? ¿Cuál es la terminología?

Quizás solo funcione? (No lo he probado)

Gracias.

Respuesta

8

estoy bastante seguro de que no hay ninguna combinación de enlaces de WCF que le permitirán literalmente de escritura en la corriente del cliente.Parece lógico que así sea, dado que en algún lugar debajo de la superficie definitivamente va a haber un NetworkStream, pero una vez que comiences a agregar encriptación y todas esas cosas divertidas, WCF debería saber cómo envolver esta secuencia para convertirla en el mensaje "real", que no creo que lo haga.

Sin embargo, el escenario I-need-to-return-a-stream-but-component-X-wants-to-write-to-one es uno común, y tengo una solución catch-all que utilizar para este escenario, que es crear una secuencia bidireccional y generar un hilo de trabajo para escribir en él. El más fácil y más seguro manera de hacerlo (por lo que quiero decir, la forma en que implica escribir el menor código y, por lo tanto, es menos probable que tenga errores) es utilizar tuberías anónimas.

He aquí un ejemplo de un método que devuelve un AnonymousPipeClientStream que se puede utilizar en los mensajes de WCF, resultados MVC, y así sucesivamente - en cualquier lugar que desee invertir la dirección:

static Stream GetPipedStream(Action<Stream> writeAction) 
{ 
    AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream(); 
    ThreadPool.QueueUserWorkItem(s => 
    { 
     using (pipeServer) 
     { 
      writeAction(pipeServer); 
      pipeServer.WaitForPipeDrain(); 
     } 
    }); 
    return new AnonymousPipeClientStream(PipeDirection.In, pipeServer.ClientSafePipeHandle); 
} 

Un ejemplo de uso de este haría ser:

static Stream GetTestStream() 
{ 
    string data = "The quick brown fox jumps over the lazy dog."; 
    return GetPipedStream(s => 
    { 
     StreamWriter writer = new StreamWriter(s); 
     writer.AutoFlush = true; 
     for (int i = 0; i < 50; i++) 
     { 
      Thread.Sleep(50); 
      writer.WriteLine(data); 
     } 
    }); 
} 

Sólo dos advertencias acerca de este enfoque:

  1. Fallará si el writeAction hace algo para eliminar el flujo (es por eso que el método de prueba en realidad no elimina el StreamWriter - porque eso eliminaría el flujo subyacente);

  2. Podría fallar si el writeAction no escribe realmente ningún dato, porque WaitForPipeDrain volverá inmediatamente y creará una condición de carrera. (Si esto es una preocupación, puede codificar un poco más a la defensiva para evitar esto, pero que complica mucho las cosas para un caso extremo raro.)

Esperemos que ayuda en su caso específico.

+0

PD Debería haber agregado la respuesta: hacer un balance de la cantidad de datos que realmente se necesita transmitir. Esto es útil para varios MB o más, pero si solo tiene unos pocos KB para enviar, costaría menos crear un 'MemoryStream' para que el componente lo escriba y luego devolverlo. – Aaronaught

+0

Whoa, sí !. Ahora déjame entender esto. Por favor confirmar; (1) El Stream de AnonymousPipe {Client, Server} es un par de clases que se usarán solo en el servidor. (2) Lo que se devuelve al cliente es un System.IO.Stream. – Cheeso

+0

ps: He hecho esta pregunta antes, vea http://stackoverflow.com/questions/1499520. Estoy muy contento de obtener una respuesta general simple. – Cheeso

2

La transmisión por WCF funciona en ambos sentidos: sus clientes pueden cargar cosas en una transmisión, pero su servicio WCF también puede enviar cosas en una transmisión. Definitivamente, puede escribir un servicio que transmita archivos de gran tamaño en función de su nombre de archivo o algún ID o algo, absolutamente.

Si desea enviar datos desde el servicio, debe hacerlo en el valor de retorno de su método de servicio, que debe ser del tipo Stream - no puede (hasta donde yo sé) "suministrar" un secuencia para escribir: el servicio WCF creará una secuencia de respuesta y la enviará de regreso.

¿Has comprobado MSDN Streaming Message Transfer o David Wood's blog post o Kjell-Sverre's blog post en la transmisión WCF? Todos ellos muestran muy bien qué configuración de configuración necesita (básicamente configurando TransferMode en su configuración de enlace en Streamed, StreamedRequest o StreamedResponse).

+0

Entonces, Marc, lo que dices es ... para una respuesta transmitida, un servicio debe devolver un flujo al cliente. En otras palabras, no hay un análogo directo a ASPNET Response.OutputStream, que un servicio podría escribir directamente en. ¿Correcto? – Cheeso

+0

@ Cheeso: correcto: el servicio debe devolver una secuencia, pero no puede simplemente escribir en una secuencia en el cliente; el servidor y el cliente no tienen ninguna conexión permanente en WCF; todo lo que puede hacer es intercambiar mensajes serializados (como todo, o de manera fragmentada como con la transmisión) –

Cuestiones relacionadas