如何实现WCF流操作,该操作将数据流入,处理,流回(使用MessageContracts)?

时间:2015-03-03 10:13:13

标签: c# .net wcf stream messagecontract

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

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

提前提出问题:

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

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

  3. 详情

    因为我需要使用MetaData同时包含输入和返回流,所以我根据需要使用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实现的示例。样本版本可以获得预期的回报。

    更新

    • 我注意到服务引用具有使用预期方法签名生成的Task / Asynchronous方法。也许这意味着当使用带有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();
      

1 个答案:

答案 0 :(得分:1)

我很抱歉回答(我没有评论的声誉)。

我正在研究simmilar项目 - 我有服务,接受大量数据流(使用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();
            }
        }