WSE3.0框架提供了数据优化传输机制,WSE3.0构建Web服务安全(4):MTOM消息传输优化和文件上传、下载 疑问里进行了介绍。WCF同样也提供了流操作来支持大数据对象的传输和处理优化机制,今天我们WCF分布式开发步步为赢系列的(4):使用流操作(Streaming Operations)优化传输。本节会详细介绍流操作的相关概念、编程实现过程,以及实际开发过程中需要主要的一些问题。本节结构:【1】流处理的概念【2】流处理的特点【3】示例代码分析【4】总结。最后上传本文的示例代码。

Streaming,本文翻译为流处理(张逸兄翻译的Programming WCF Services一书里把这个机制翻译为“流操作”,不存在争议。我选择“流处理”一词的意思,只是想把这个Streaming词形象准确化,动名词,流化处理、流处理,翻译为流操作,初学者会误会认为这个是一个服务操作。因为Streaming只是WCF内建的一个机制。而不是操作。)

我们首先来理解什么是Streaming流处理。

【1】Streaming流处理的概念:

通常情况,客户端和服务端进行交互,传递消息,都是放到接收端的缓存里,待接收完毕后再进行处理。无论接收端是客户端还是服务端都是如此。

【1.1】要解决的问题:

当客户端调用服务时,要阻塞客户单进程,直到消息发送完毕,服务端才开始处理数据,然后是返回处理完毕的结果给客户端,客户端接收完毕,才能解除阻塞。这样带来的问题是当消息传递的时间很短,相对处理时间可以忽略不计,不会影响系统服务的效率。但是要是消息数据很大,比如是图片或者多媒体对象。每次传输时间相对较大,这样接收端的等待时间过久,势必每次阻塞都会很长,进程无法继续执行。因而导致效率低下。

【1.2】Streaming流处理:

Streaming流处理就是WCF提供的主要针对大量消息数据处理的一种优化机制。WCF允许接收端通过通道接受消息的同时,启动对消息数据的处理,这样的过程称为流传输模型。

【2】Streaming流处理的特点:

显然对于处理大量的消息数据而言,流处理机制改善了系统的吞吐量和响应效率。

【2.1】流处理操作定义:

WCF的流处理机制需要使用.NET FrameWork定义的Stream类(它是FileStreamNetworkStreamMemoryStream 的父类)。流处理适用一下场景:


[ServiceContract]
interface IMyContract
{
   [OperationContract]
   Stream StreamReply1( );    [OperationContract]
   void StreamReply2(out Stream stream);    [OperationContract]
   void StreamRequest(Stream stream);    [OperationContract(IsOneWay = true)]
   void OneWayStream(Stream stream);
}

它可以做为返回数据、参数、输出参数的类型。当然也可以作为单调服务的操作参数。这里使用的参数必须是可序列化的,例如MemoryStream。而FileStream不支持序列化因而不能作为参数或者返回数据的类型。

【2.2】流处理与绑定协议:

流处理机制在特定的绑定协议中才能使用,目前是BasicHttpBindingNetTcpBinding, 和NetNamedPipeBinding 支持流处理模型。但是在默认情况下,WCF禁止流处理模式。

流传输模式使用使用TransferMode进行配置,TransferMode为枚举类型,其定义如下:


    public enum TransferMode
    {
        // Summary:
        //     The request and response messages are both buffered.
        Buffered = 0,
        //
        // Summary:
        //     The request and response messages are both streamed.
        Streamed = 1,
        //
        // Summary:
        //     The request message is streamed and the response message is buffered.
        StreamedRequest = 2,
        //
        // Summary:
        //     The request message is buffered and the response message is streamed.
        StreamedResponse = 3,
    }

只有Streamed模式支持2.1中列举的流处理模式场景。除了直接在服务上配置属性以外,我们还可以再服务的配置文件里定义流传输模式。代码如下:


      <basicHttpBinding>
        <binding name="basicHttpBinding"   receiveTimeout="10:10:10"  transferMode="Streamed" maxReceivedMessageSize="200000">
        </binding>
      </basicHttpBinding>
      <netTcpBinding>
        <binding name="netTcpBinding"  receiveTimeout="10:10:10" transferMode="Streamed" maxReceivedMessageSize="200000">
        </binding>
      </netTcpBinding>

此为托管宿主的配置文件,特定的绑定协议,可以配置其传输模式。

【2.3】注意:

流处理在使用http协议时,其默认消息长度是64K,如果希望增加数据长度,需要在配置文件里重新设置。如: maxReceivedMessageSize="200000",具体代码如下:

<basicHttpBinding>
        <binding name="basicHttpBinding"   receiveTimeout="10:10:10"  transferMode="Streamed" maxReceivedMessageSize="200000">
        </binding>
      </basicHttpBinding>

【3】示例代码分析:

这里测试的流处理机制,使用的是处理图片的上传于下载,分别使用Stream和其子类MemoryStream作为参数或者返回消息数据的类型。基本代码演示的是流处理三种模式场景:

【3.1】服务端:

服务契约分别定义了下载数据和上传数据,下载数据使用的类型MemoryStream,上传数据参数类型是是Stream。具体代码如下:


 //1.服务契约
    [ServiceContract( Namespace = "http://www.cnblogs.com/frank_xl/")]
    public interface IWCFService
    {
        //操作契约,获取数据流
        [OperationContract]
        MemoryStream DownLoadStreamData(string fileName);         //操作契约,输出数据流
        [OperationContract]
        void DownLoadStreamDataOut(out MemoryStream stream, string fileName);         //操作契约,上载数据流,单向操作的消息转换为数据流
        [OperationContract(IsOneWay=true)]
        void UpLoadStreamData(Stream stream);
    }
    //2.服务类,继承接口。实现服务契约定义的操作
    public class WCFService : IWCFService
    {
        //1实现接口定义的方法,下载文件数据流
        public MemoryStream DownLoadStreamData(string fileName)
        {
           // Stream stream = 
            byte[] file = new byte[200000];
            String filePath = AppDomain.CurrentDomain.BaseDirectory + @"/" + fileName;
            file = File.ReadAllBytes(filePath);
            MemoryStream memoryStream = new MemoryStream(file);
            return memoryStream;
        }
        //2实现接口定义的方法,下载文件数据流
        public void DownLoadStreamDataOut(out MemoryStream stream, string fileName)
        {
            // Stream stream = 
            byte[] file = new byte[200000];
            String filePath = AppDomain.CurrentDomain.BaseDirectory + @"/" + fileName;
            file = File.ReadAllBytes(filePath);
            MemoryStream memoryStream = new MemoryStream(file);
            stream = memoryStream;
            
        }
        //3实现接口定义的方法,上传文件数据流
        public void UpLoadStreamData(Stream stream)
        {
            // codes here to deal with the stream Stream stream = 
            Console.WriteLine("The Stream length is {0}",stream.Length);         }
    }

【3.2】托管宿主:

我们分别使用basicHttpBinding和netTcpBinding定义了两个服务终结点,这里不要忘记设置最大接受消息数据大小maxReceivedMessageSize="200000",如果设置较小会导致接受数据超过设定的错误。具体代码如下:


  <system.serviceModel>
    <services>
      <service behaviorConfiguration="WCFService.WCFServiceBehavior"
        name="WCFService.WCFService">
        <endpoint 
          address="http://localhost:8002/WCFService" 
          binding="basicHttpBinding" 
          contract="WCFService.IWCFService">
        </endpoint>
        <endpoint
         address="net.tcp://localhost:8004/WCFService"
         binding="netTcpBinding"
         contract="WCFService.IWCFService">
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <endpoint address="mex"  binding="mexTcpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8001/"/>
            <add baseAddress="net.tcp://localhost:8003/"/>
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="WCFService.WCFServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <basicHttpBinding>
        <binding name="basicHttpBinding"   receiveTimeout="10:10:10"  transferMode="Streamed" maxReceivedMessageSize="200000">
        </binding>
      </basicHttpBinding>
      <netTcpBinding>
        <binding name="netTcpBinding"  receiveTimeout="10:10:10" transferMode="Streamed" maxReceivedMessageSize="200000">
        </binding>
      </netTcpBinding>
    </bindings>
  </system.serviceModel>

【3.3】客户端:

客户端分别测试数据的上传和下载,使用不同的方法。这里的测试目前

【4】总结:

本文介绍了WCF流处理模型,但是实现代码出现问题。

(1)作为WCF的流处理机制,确实为我们的大规模消息数据传输提供了理想的机制,提高了系统的效率和响应速度。

(2)Stream作为.net类库的内部类型,在.net平台上使用来说较为方便,但是与异构平台的交互势必受到诸多限制,也违背了WCF跨平台的初衷。

(3) 我在调试这个示例代码的过程中遇到了几个错误,基本都整理出来放到WCF分布式开发常见错误里了。使用netTcpBinding绑定进行数据传输的时候,一个很有价值的错误就是:the socket connection was aborted. this could be caused by an error processing your message or a receive timeout being exceeded by the remote host ...这个错误我google国内和国外的一些资料,但是解决的办法都是更换协议。其实是一种很无奈的措施,目前我还没有找出更好的解决办法就是基于netTcpBinding。表面的原因是服务处理超时。但是具体的错误信息没有这样简单。我更换协议以后其他的流服务操作调用出了问题。所以这个只能针对特定的操作有帮助。我把这个错误收集起来供大家参考。也希望发挥大家的作用把这个问题解决。

也许这个错误应该反映给WCF的开发小组,无论国内还是国外的技术论坛,包括MSDN都有人遇到这样的问题,而没有一个最佳的解决方案。这里我对流处理示例代码分别打包,目前都有问题。

<1>流处理机制示例1里代码测试上传文件,成功,下载文件错误。

/Files/frank_xl/WCFServiceStreamingFrankXuLei.rar

<2>这里使用的是字节数组,测试下载文件,下载文件是成功的,上传文件失败。/Files/frank_xl/WCFServiceStreamingByteArrayFrankXuLei.rar

两个失败的原因都是一样,套接字中断,连接超时。WCF分布式开发常见错误解决(10):套接字连接中断,The socket connection was aborted ,我进行了整理,也查找了国外的论坛,没有找到理想的解决办法。我已经尝试了修改接受时间的限制,但是不起作用。我会继续关注这个问题,也希望有兴趣的朋友补充。MSDN论坛上有人提供了解决的方法,但是不理想,更换协议。希望微软WCF的开发、测试小组早日注意这个问题。

<3>Streaming代码修改完毕,测试成功,请大家下载新的文件参考:/Files/frank_xl/WCFServiceStreamingSuccefullyFrankXuLei.rar 
  以前代码因为配置错误,绑定的配置没应用对。我今天修改完毕。
 参考文章:

1.《Programming WCF Services》;

2.WSE3.0构建Web服务安全(4):MTOM消息传输优化和文件上传、下载

WCF分布式开发步步为赢(11):WCF流处理(Streaming)机制的更多相关文章

  1. WCF分布式开发步步为赢(7):WCF数据契约与序列化

    本节继续学习WCF分布式开发步步为赢(7):WCF数据契约与序列化.数据契约是WCF应用程序开发中一个重要的概念,毫无疑问实现客户端与服务端数据契约的传递中序列化是非常重要的步骤.那么序列化是什么?为 ...

  2. WCF分布式开发步步为赢(3)WCF服务元数据交换、配置及编程开发

    今天我们继续WCF分布式开发步步为赢(3)WCF服务元数据交换.配置及编程开发的学习.经过前面两节的学习,我们了解WCF分布式开发的相关的基本的概念和自定义宿主托管服务的完整的开发和配置过程.今天我们 ...

  3. WCF分布式开发步步为赢(13):WCF服务离线操作与消息队列MSMQ

    之前曾经写过一个关于MSMQ消息队列的文章:WCF分布式开发必备知识(1):MSMQ消息队列 ,当时的目的也是用它来作为学习WCF 消息队列MSMQ编程的基础文章.在那篇文章里,我们详细介绍了MSMQ ...

  4. WCF分布式开发步步为赢(12):WCF事务机制(Transaction)和分布式事务编程

    今天我们继续学习WCF分布式开发步步为赢系列的12节:WCF事务机制(Transaction)和分布式事务编程.众所周知,应用系统开发过程中,事务是一个重要的概念.它是保证数据与服务可靠性的重要机制. ...

  5. WCF分布式开发步步为赢(4):WCF服务可靠性传输配置与编程开发

    今天继续WCF分布式开发步步为赢系列的第4节:WCF服务可靠性传输配置与编程开发.这个章节,我们要介绍什么是WCF服务的可靠性传输,随便介绍网络协议的概念,Web Service为什么不支持可靠性传出 ...

  6. WCF分布式开发步步为赢(9):WCF服务实例激活类型编程与开发

    .Net Remoting的激活方式也有三种:SingleTon模式.SingleCall模式.客户端激活方式,WCF服务实例激活类型包括三种方式:单调服务(Call Service),会话服务(Se ...

  7. WCF分布式开发步步为赢(14):WCF安全编程--基本概念

    WCF安全机制是个非常复杂的问题,因为涉及的知识点较多,所以今天这个文章,会分析进行WCF安全开发应该了解的哪些知识点.如何查看资料.为了更好地理解WCF安全相关知识,我把WCF安全机制主要知识点整理 ...

  8. WCF分布式开发步步为赢(10):请求应答(Request-Reply)、单向操作(One-Way)、回调操作(Call Back).

    WCF除了支持经典的请求应答(Request-Reply)模式外,还提供了什么操作调用模式,他们有什么不同以及我们如何在开发中使用这些操作调用模式.今天本节文章里会详细介绍.WCF分布式开发步步为赢( ...

  9. WCF分布式开发步步为赢(8):使用数据集(DataSet)、数据表(DataTable)、集合(Collection)传递数据

    数据集(DataSet).数据表(DataTable).集合(Collection)概念是.NET FrameWork里提供数据类型,在应用程序编程过程中会经常使用其来作为数据的载体,属于ADO.NE ...

随机推荐

  1. Linux下安装MySQLdb模块

    1,查看是否已安装MySQLdb模块 进入python的命令行,输入 import MySQLdb 如果没有报错,证明此模块已经安装,可以跳过以下步骤. 2,下载最新的MySQLdb安装包: wget ...

  2. iOS耳机操作

    iOS在7之后增加的麦克风权限的申请,代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 AVAudioSession *avSession = [AVAudioSession shar ...

  3. Linux下如何使CP命令不提示覆盖

    在Linux下使用CP命令,经常会提示是否覆盖,如果是太批量的文件覆盖,老是这么提示,会很烦的.那如何解决这个问题呢? 我们先来看一下原因吧! 一般我们使用的命令是cp -rf sourcefile ...

  4. 单点登录 关于Cookie跨域的问题

    public void ProcessRequest(HttpContext context) { HttpCookie cookie = new HttpCookie("name" ...

  5. 实例详细说明linux下去除重复行命令uniq

    地址:http://blog.51yip.com/shell/1022.html 一,uniq干什么用的 文本中的重复行,基本上不是我们所要的,所以就要去除掉.linux下有其他命令可以去除重复行,但 ...

  6. ORACLE 检查数据库表中是否存在不规范字 段的语句参考.sql

    --查看是否有除number,char,date,varchar2,clob/blob之外的类型,比如:NVARCHAR2,TIMESTAMP(6),FLOATSELECT DISTINCT a.DA ...

  7. C#如何设置Listview的行高-高度

    Winform窗口中,控件listview是无法设置行高的. 以加入一个imagelist(图片列表控件)实现行高的设置. ImageList imageList = new ImageList(); ...

  8. android 开发-自定义多节点进度条显示

    看效果图: 里面的线段颜色和节点图标都是可以自定义的. main.xml <RelativeLayout xmlns:android="http://schemas.android.c ...

  9. 使用sqlserver日期函数获取当前日期

    使用sqlserver日期函数中的getdate()可以获取当现的日期,下面就将为您介绍这种使用sqlserver日期函数获取当前日期的方法,供您参考,希望对您学习sqlserver日期函数有所启迪. ...

  10. MySQL用程序代码建表(1)

    一.创建表格代码格式 create table <表名>( <列名> <数据类型及长度> [not null], <列名> <数据类型及长度> ...