【问题标题】:How do I implement a WCF Streaming operation that streams data in, processes it, streams it back(with MessageContracts)?如何实现 WCF 流操作,将数据流入、处理、流回(使用 MessageContracts)?
【发布时间】:2015-05-03 22:40:59
【问题描述】:

我需要一个 WCF 服务操作,它接受一个大的流,处理它并返回那个流。

我使用an MSDN article on Large Data Streaming 作为我需要的参考。我听从了那篇文章中的建议。

提前问问题:

  1. 我想知道为什么生成的服务操作在合同中指定的时候没有返回类型?

  2. 如果这是预期行为,我应该如何让它传递一个流并返回一个已处理的流?

详情

因为我需要用元数据伴随输入和返回流,所以我根据需要用 MessageContract 属性修饰了类。

以下是我的实现的简要介绍:

消息合同:

[MessageContract]
public class InputStreamMessage
{
    [MessageHeader]
    public InputStreamHeader Header { get; set; }

    [MessageBodyMember(Order = 1)]
    public Stream Data { get; set; }

}

[MessageContract]
public class OutputStreamMessage
{
    [MessageHeader]
    public OutputStreamHeader Header { get; set; }

    [MessageBodyMember(Order = 1)]
    public Stream Data { get; set; }

}

服务合同:

[ServiceContract]
public interface IStreamService
{
    [OperationContract]
    OutputStreamMessage ProcessStream(InputStreamMessage input);
}

服务实施:

 public OutputStreamMessage DoStreamOperation(InputStreamMessage input)
 {
    //Some logic that assigns re
    OutputStreamMessage output = DoSomeNonBufferedProcessing(input);

    return output;
 }

客户端:

在客户端,然后生成服务引用,并调用服务如下:

private void PerformStreamOperation()
{
    try
    {
        //
        StreamServiceReference.StreamServiceClient client = new StreamServiceReference.StreamServiceReferenceClient();
        client.Open();

        //Set Header and Parameters
        InputMessageHeader header = new InputMessageHeader();

        //...                
        //... initialize header data here
        //...                

        //... do some operation to get input stream
        var inputstream = SomeOperationToGetInputStream();

        //Perform Service stream action
        //         ____ [ Why does the generated method have the following signature, retuning void?]
        //        |     [ If this is expected, how do I use it? ]
        //        |
        //        V 
        client.DoStreamOperation(header, ref inputstream); 


        //...                
        //... Do what you wish with data
        //...                

    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message.ToString(), "Stream Processing Error");
    }
}

MSDN 文章使用与official WCF samples. 中存在的完全相同的合同

Stream EchoStream(流数据)

但没有等效 MessageContract 实现的示例。示例版本实现了预期回报。

更新

  • 我注意到服务引用具有使用预期方法签名生成的任务/异步方法。也许这意味着当使用带有 Stream 属性的 MessageContract 时,返回一个类似结构的对象,那么您将不得不异步调用它。我没有在任何地方看到它记录在案。将尝试使用这些方法 - 没有按照我们想要的同步操作工作。
  • 我也尝试过使用ChannelFactory 作为生成代理客户端的替代方案:

      EndpointAddress endpoint = new EndpointAddress("net.tcp://localhost:9910/StreamService");
    
      channelFactory = new ChannelFactory<IStreamService>("netTcpStreamedEndPoint");
      channelFactory.Endpoint.Contract.SessionMode = SessionMode.Allowed;
      IStreamService service = channelFactory.CreateChannel();
    

【问题讨论】:

  • 您是否尝试过使用ChannelFactory 而不是添加服务引用,这可能有助于显示问题所在。
  • 是的,我用过ChannelFactory。将进行更新
  • 好的,使用ChannelFactory的结果是什么,您是否尝试过打开跟踪并查看是否弹出任何错误?
  • 将用我尝试过的内容更新问题

标签: c# .net wcf stream messagecontract


【解决方案1】:

很抱歉在回答中回复(我没有 cmets 的声誉)。

我正在从事与您类似的项目 - 我有服务,它接受大量数据流(使用 MessageContracts),对其进行处理,然后客户端可以下载这些数据。

首先——输入参数在:

 client.DoStreamOperation(header, ref inputstream); 

表明,您似乎没有生成包含 MessageContracts 的服务代理(请参阅http://blogs.msdn.com/b/zainnab/archive/2008/05/13/windows-communication-foundation-wcf-what-the-hell-is-always-generate-message-contracts.aspx)。这应该在客户端为您提供 OutputStreamMessage 和 InputStreamMessage 合同。

正确生成 messageContracts,我可以在我的代码中编写这两个,而不会收到编译错误:

client.DoStreamOperation(inputStreamMessage)

   StreamServiceReference.StreamServiceClient.OutputStreamMessage outputMessage = client.DoStreamOperation(inputStreamMessage)

但基本上第一个没有用。 当然,我必须先创建 InputStreamMessage 对象:

StreamServiceReference.StreamServiceClient.InputStreamMessage inputStreamMessage = new StreamServiceReference.StreamServiceClient.InputStreamMessage();

如果您愿意,我可以发布一些我的 MessageContracts 示例。

另外,请看这篇文章:http://www.codeproject.com/Articles/166763/WCF-Streaming-Upload-Download-Files-Over-HTTP。我的消息合同在项目早期看起来很相似


编辑: 会话模式设置如下:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]

这样做的原因是我需要维护有关对象的信息(状态),这对于多个客户端来说很常见。但这不应该影响流式传输。

这是我的绑定。我使用基本的http:

      <basicHttpBinding>
        <binding name="TransferBinding" transferMode="Streamed" maxReceivedMessageSize="10067108864">
        </binding>
      </basicHttpBinding>

对于上传,我使用这种消息契约:

    [MessageContract]
        public class RemoteFileInfo : IDisposable
        {
            [MessageHeader(MustUnderstand = true)]
            public string FileName;

            [MessageBodyMember]
            public System.IO.Stream FileByteStream;
}

这是在客户端定义的方法体,调用 StartUpload() 在服务端定义(您需要定义指向您要上传的文件的 filePath):

using (System.IO.FileStream stream = new System.IO.FileStream(filePath, FileMode.Open, FileAccess.Read))
        {
                           // start service client
                CalculationServiceClient client = new CalculationServiceClient();     

                RemoteFileInfo remoteFileInfo = new RemoteFileInfo(); ;
                remoteFileInfo.FileName = TextBox1.Text;
                remoteFileInfo.FileByteStream = stream;

                // upload file
                client.StartUpload(remoteFileInfo);

                // close service client
                client.Close();
                uploadStream.Close();
            }
        }

然后,我在服务端定义 StartUpload() operationContract。 StartUpload 合约的内部看起来像这样:

public void StartUpload(RemoteFileInfo fileInfo)
        {

            string filePath = define your filePath, where you want to save the file;           

            int chunkSize = 2048;
            byte[] buffer = new byte[chunkSize];

            using (System.IO.FileStream writeStream = new System.IO.FileStream(filePath, System.IO.FileMode.CreateNew, System.IO.FileAccess.Write))
            {
                do
                {
                    // read bytes from input stream (provided by client)
                    int bytesRead =  fileInfo.FileByteStream.Read(buffer, 0, chunkSize);
                    if (bytesRead == 0) break;

                    // write bytes to output stream
                    writeStream.Write(buffer, 0, bytesRead);
                } while (true);

                writeStream.Close();
            }
        }

【讨论】:

  • 嗨@JakubJ。感谢回复。你介意发布一个示例实现吗?您还使用了什么SessionMode,您的绑定配置如何?它是连续的数据流,还是在服务操作的某个时间点关闭它?代码项目文章不是我想要的。我想发送和返回一个连续的、不间断的流
  • 你好。连续流到底是什么意思?例如,如果您正在流式传输实时视频?我正在使用流传输大文件 - 客户端选择要传输的文件,点击“上传”按钮,整个文件就会流式传输到服务端。我将在几分钟内更新我的答案,并提供我的一些实现。
  • 我的意思是流中没有中断。当我输入流时,它会被处理并随时返回。类似于通常使用的方法签名void ProcessStream(Stream input, Stream output),其中ProcessStream处理并逐字节写入处理后的input流到output。没有Stream.Close() 内部操作。这在一个应用程序的上下文中很好。但与 WCF 完全不同。问题是,使用transferMode=Streamed,您可以拥有一个且只有一个参数(原始StreamMessageContract 中的组合)。
  • 哦,我明白了...所以您基本上不想将其分为3个步骤:上传(停止),处理数据(停止),下载数据,对吧?我已经用我拥有的一些代码更新了我的帖子,但我没有意识到你想要别的东西,而不是我已经实现的。恐怕我不知道如何实现您需要的场景。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-21
  • 2011-09-22
  • 1970-01-01
相关资源
最近更新 更多