2010-04-28 51 views
12

Có đúng là trong WCF, tôi không thể có dịch vụ viết cho luồng mà khách hàng nhận được không?WCF và các yêu cầu và phản hồi trực tuyến

Phát trực tuyến được hỗ trợ trong WCF cho các yêu cầu, phản hồi hoặc cả hai. Tôi muốn hỗ trợ một kịch bản mà bộ tạo dữ liệu (hoặc máy khách trong trường hợp yêu cầu được truyền trực tuyến hoặc máy chủ trong trường hợp phản hồi được truyền trực tiếp) có thể ghi trên luồng. Điều này có được hỗ trợ không?

Tương tự là Response.OutputStream từ một yêu cầu ASP.NET. Trong ASPNET, bất kỳ trang nào cũng có thể gọi Write trên luồng đầu ra và nội dung được khách hàng nhận. Tôi có thể làm một cái gì đó tương tự trong một dịch vụ WCF - gọi Viết trên một luồng mà khách hàng nhận được không?

Hãy để tôi giải thích bằng hình minh họa WCF. Ví dụ đơn giản nhất về Streaming trong WCF là dịch vụ trả về một FileStream cho một máy khách. Đây là phản hồi được truyền trực tiếp. Mã máy chủ để thực hiện điều này, là như thế này:

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

Và mã khách hàng là như thế này:

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); 

(ví dụ lấy từ http://blog.joachim.at/?p=33)

Rõ ràng, phải không? Máy chủ trả về luồng cho máy khách và máy khách gọi Đọc trên đó.

Có thể cho khách hàng cung cấp Luồng và máy chủ gọi Ghi trên đó không?
Nói cách khác, thay vì mô hình kéo - nơi khách hàng kéo dữ liệu từ máy chủ - đó là mô hình đẩy, nơi khách hàng cung cấp luồng "chìm" và máy chủ ghi vào đó. Mã phía máy chủ có thể trông giống như sau:

[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); 
    } 
} 

Điều này có thể thực hiện được trong WCF và nếu có, làm cách nào? Cài đặt cấu hình cần thiết cho ràng buộc, giao diện, v.v. là gì? Thuật ngữ là gì?

Có thể nó sẽ hoạt động? (Tôi chưa thử)

Cảm ơn.

Trả lời

8

Tôi là khá chắc chắn rằng không có sự kết hợp các ràng buộc WCF sẽ cho phép bạn ghi nghĩa là ghi vào luồng của khách hàng.Nó có vẻ hợp lý mà phải có, cho rằng một nơi nào đó bên dưới bề mặt chắc chắn sẽ là một NetworkStream, nhưng một khi bạn bắt đầu thêm mã hóa và tất cả những thứ thú vị, WCF sẽ phải biết làm thế nào để quấn dòng này để biến nó thành thông điệp "thực sự" mà tôi không nghĩ là có.

Tuy nhiên, kịch bản I-need-to-return-a-stream-nhưng-component-X-muốn-to-write-to-one là một kịch bản phổ biến và tôi có một giải pháp toàn diện mà tôi sử dụng cho kịch bản này, đó là tạo một luồng hai chiều và sinh ra một chuỗi công nhân để ghi vào nó. dễ nhấtan toàn nhất cách để làm điều này (theo ý tôi là, cách liên quan đến việc viết mã ít nhất và do đó ít có khả năng bị lỗi) là sử dụng các đường ẩn danh.

Dưới đây là một ví dụ về một phương pháp mà trả về một AnonymousPipeClientStream có thể được sử dụng trong các tin nhắn WCF, kết quả MVC, và vân vân - bất cứ nơi nào bạn muốn đảo ngược hướng:

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); 
} 

Một cách sử dụng ví dụ về điều này sẽ là:

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); 
     } 
    }); 
} 

Chỉ hai hãy cẩn thận về cách tiếp cận này:

  1. Nó sẽ thất bại nếu writeAction làm bất cứ điều gì để xử lý các dòng (đó là lý do tại sao phương pháp thử nghiệm không thực sự xử lý các StreamWriter - bởi vì điều đó sẽ xử lý các dòng cơ bản);

  2. Có thể không thành công nếu writeAction không thực sự ghi bất kỳ dữ liệu nào, vì WaitForPipeDrain sẽ trả về ngay lập tức và tạo điều kiện cho cuộc đua. (Nếu đây là một mối quan tâm, bạn có thể mã hóa một chút phòng thủ để tránh điều này, nhưng nó làm phức tạp mọi thứ rất nhiều cho một trường hợp cạnh hiếm.)

Hy vọng rằng sẽ giúp trong trường hợp cụ thể của bạn.

+0

PS Tôi nên thêm vào câu trả lời - lấy số lượng dữ liệu thực sự cần phải được phát trực tuyến. Điều này rất hữu ích cho một vài MB hoặc nhiều hơn, nhưng nếu bạn chỉ có một vài KB để gửi thì nó sẽ chi phí ít hơn để chỉ tạo ra một 'MemoryStream' cho thành phần của bạn để ghi vào và sau đó trả lại điều đó. – Aaronaught

+0

Whoa, yes !. Bây giờ hãy để tôi hiểu điều này. Vui lòng xác nhận; (1) Dòng AnonymousPipe {Client, Server} là một cặp các lớp chỉ được sử dụng phía máy chủ. (2) Những gì được trả lại cho khách hàng là một System.IO.Stream. – Cheeso

+0

ps: Tôi đã hỏi câu hỏi này trước, xem http://stackoverflow.com/questions/1499520. Tôi rất vui khi nhận được một câu trả lời chung, đơn giản. – Cheeso

2

WCF phát trực tuyến hoạt động theo cả hai cách - khách hàng của bạn có thể tải nội dung lên trong luồng, nhưng dịch vụ WCF của bạn cũng có thể gửi nội dung xuống trong luồng. Bạn chắc chắn có thể viết một dịch vụ mà dòng trở lại các tập tin lớn dựa trên tên tập tin của họ hoặc một số ID hoặc một cái gì đó - hoàn toàn.

Nếu bạn muốn gửi dữ liệu trở lại từ dịch vụ, bạn cần làm như vậy trong giá trị trả lại của phương thức dịch vụ, cần phải thuộc loại Stream - bạn không thể (theo như tôi biết) "cung cấp" luồng để ghi vào - dịch vụ WCF sẽ tạo luồng phản hồi và gửi lại.

Bạn đã xem MSDN Streaming Message Transfer hoặc David Wood's blog post hoặc Kjell-Sverre's blog post trên luồng WCF chưa? Tất cả đều hiển thị độc đáo những gì bạn cần cấu hình cài đặt cấu hình (về cơ bản thiết lập các TransferMode trong cấu hình ràng buộc của bạn để Streamed, StreamedRequest hoặc StreamedResponse).

+0

Vì vậy, Marc, những gì bạn đang nói là ... đối với một phản hồi được truyền trực tuyến, một dịch vụ phải trả lại luồng cho khách hàng. Nói cách khác, không có tương tự trực tiếp với ASPNET Response.OutputStream, một dịch vụ có thể ghi trực tiếp vào. Chính xác? – Cheeso

+0

@Cheeso: chính xác - dịch vụ phải trả về luồng, nhưng không thể chỉ ghi vào luồng trong ứng dụng khách - máy chủ và máy khách không có bất kỳ kết nối nào đứng trong WCF - tất cả những gì bạn có thể làm là trao đổi thư được tuần tự hóa (như một toàn bộ, hoặc trong một thời trang chunked như với streaming) –

Các vấn đề liên quan