【问题标题】:WCF and streaming requests and responsesWCF 和流式处理请求和响应
【发布时间】:2011-02-13 04:01:36
【问题描述】:

在 WCF 中,我不能将服务 写入 到客户端接收的流中,这对吗?

WCF 支持流式处理请求、响应或两者。 我想支持数据生成器(在流式请求的情况下为客户端,或在流式响应的情况下为服务器)可以在流上写入的场景。这支持吗?

类比是来自 ASP.NET 请求的 Response.OutputStream。在 ASPNET 中,任何页面都可以在输出流上调用 Write,并且内容被客户端接收。我可以在 WCF 服务中执行类似的操作吗 - 在客户端接收到的流上调用 Write?

让我用一个 WCF 插图来解释。 WCF 中流式处理的最简单示例是服务将 FileStream 返回给客户端。这是一个流式响应。实现这个的服务器代码是这样的:

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

而客户端代码是这样的:

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

(示例取自http://blog.joachim.at/?p=33

清楚,对吧?服务器将 Stream 返回给客户端,客户端在其上调用 Read。

客户端是否可以提供一个 Stream,而服务器可以在其上调用 Write?
换句话说,它不是一个拉模型——客户端从服务器拉数据——它是一个推模型,客户端提供“接收器”流并且服务器写入它。服务器端代码可能类似于:

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

这在 WCF 中是否可行,如果可以,如何实现?绑定、接口等需要哪些配置设置?术语是什么?

也许它会起作用? (没试过)

谢谢。

【问题讨论】:

    标签: .net wcf streaming


    【解决方案1】:

    相当确定没有任何 WCF 绑定组合可以让您字面上写入客户端的流。应该有似乎是合乎逻辑的,因为在表面之下的某个地方肯定会有NetworkStream,但是一旦你开始添加加密和所有有趣的东西,WCF 就必须知道如何包装这个流来打开它进入“真实”的信息,我认为它不会。

    但是,I-need-to-return-a-stream-but-component-X-wants-to-write-to-one 场景很常见,我有一个包罗万象的解决方案对于这种情况,即创建一个双向流并产生一个工作线程来写入它。做到这一点的最简单最安全的方法(我的意思是,涉及编写最少代码并因此最不可能出错的方法)是使用匿名管道。

    这是一个返回 AnonymousPipeClientStream 的方法示例,该方法可用于 WCF 消息、MVC 结果等 - 任何您想要反转方向的地方:

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

    这个例子的用法是:

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

    关于这种方法只有两个注意事项:

    1. 如果writeAction 执行任何处理流的操作,它将失败(这就是为什么测试方法实际上并未处理StreamWriter - 因为这会处理底层流);

    2. 如果writeAction 实际上没有写入任何数据,它可能会失败,因为WaitForPipeDrain 将立即返回并创建竞争条件。 (如果这是一个问题,您可以稍微防御性地编写代码来避免这种情况,但对于罕见的边缘情况,它会使事情复杂化很多。)

    希望对您的具体情况有所帮助。

    【讨论】:

    • P.S.我应该在答案中添加 - 评估真正需要流出多少数据。这对于几 MB 或更多的内存很有用,但如果您只需要发送几 KB,那么只需创建一个 MemoryStream 供您的组件写入然后返回它,成本会更低。
    • 哇,是的!现在让我明白这一点。请确认; (1) AnonymousPipe{Client,Server}Stream 是一对仅在服务器端使用的类。 (2) 返回给客户端的是一个System.IO.Stream。
    • ps:这个问题我以前问过,见stackoverflow.com/questions/1499520。我很高兴得到一个笼统、简单的答案。
    • @Cheeso:(1) 当然,匿名管道可以跨进程使用,但它们非常适合进程内使用。它们不像纯线程方法那样“苗条”,但对于非常大的流,开销变得微不足道。 (2) 绝对是,PipeStream 类都来自System.IO.Stream
    • @Aaron,感谢您的洞察力。更多问题:“纯线程方法”是什么意思?假设我们有 I-need-to-return-a-stream-but-component-X-wants-to-write-to-one 的情况,正如你所说,我无法想象一个没有的解决方案涉及一个 bg 线程,以及一些协调读写的信号。我实际上已经编写了一个适配器流来解决这个问题,但它并没有得到强化,而且代码比我想要拥有的要多得多。这种匿名管道方法似乎简单易行。那么 - “纯线程方法”我缺少什么?
    【解决方案2】:

    WCF 流式传输有两种方式 - 您的客户端可以在流中上传内容,但您的 WCF 服务也可以在流中向下发送内容。您绝对可以编写一个服务,根据文件名或某些 ID 或其他内容流回大文件 - 绝对如此。

    如果你想从服务发回数据,你需要在你的服务方法的返回值中这样做,它需要是Stream类型 - 你不能(据我所知)“供应”要写入的流 - WCF 服务将创建响应流并将其发送回。

    您是否查看过有关 WCF 流的 MSDN Streaming Message TransferDavid Wood's blog postKjell-Sverre's blog post?它们都很好地显示了您需要的配置设置(基本上将绑定配置中的TransferMode 设置为StreamedStreamedRequestStreamedResponse)。

    【讨论】:

    • 所以,Marc,您的意思是……对于流式响应,服务必须将 Stream 返回给客户端。换句话说,没有与 ASPNET Response.OutputStream 直接类似的服务可以直接写入。对吗?
    • @Cheeso:正确-服务必须返回一个流,但不能只写入客户端中的流-服务器和客户端在 WCF 中没有任何常设连接-您可以做的一切是交换序列化的消息(作为一个整体,或者像流式传输一样以分块的方式)
    猜你喜欢
    • 2011-02-23
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-02
    • 1970-01-01
    • 2013-03-30
    • 1970-01-01
    相关资源
    最近更新 更多