Asp.Net教程,WinForm教程,Asp.Net MVC,vs2008教程,vs2010教程,Silverlight技术,源码下载,Asp.Net视频教程
全站热门标签
vs2010 Silverlight 存储过程 水晶报表 ADO.NET JavaScript LINQ AjaxPro DataGridView 面向对象 Extjs GridView XML DevExpress HTML教程 Oracle jQuery 分页 GDI+ Visual C++2010 MySQL Office2010 WPF MVC Dojo WCF4.0 VB.NET Sql2005 textbox cookie WCF WinForm Discuz!NT SQL经典语句 T-SQL checkbox ASPxGridView F# asp.net SQL VS2008新特性 DropDownList Access TreeView Ajax VS2008 页面执行时间 Flex 字符串 回调 VB2005 DataSet C#时间 ASP.NET性能优化 用户在线检测 动画
Framework C#技术 VB.NET VC.NET WCFWPF
当前位置: 主页 > WinForm教程 > WPF >

WCF4.0新特性体验(7):自定义绑定实现字节流编码(ByteStream)

时间:2010-08-12 23:20来源:未知 作者:admin 点击:

WCF4.0之前的版本中已经提供了三种编码器:Text、Binary 和 MTOM 消息编码器。但是有些时候我们想传递最原始的二进制数据,不进行任何的包装,怎么才能实现呢?WCF4.0中给出了一种新的编码器。今天我们就来学习如何使用这个新的编码器。本节内容会对WCF消息编码器、自定义绑定也会做简要介绍。

【1】WCF编码器:

 我们知道在WCF4.0以前的版本中已经提供了三种编码器:Text、Binary 和 MTOM 消息编码器。当然我们也可以实现自定义编码器。Text消息编码器同时支持纯 XML 编码和 SOAP 编码。Text消息编码器的纯 XML 编码模式称为 POX(“Plain Old XML”)编码器,以便与基于文本的 SOAP 编码进行区分。关于三种编码器类型如下:

编码器类型
 描述
 
TextMessageEncodingBindingElement
 文本消息编码器,同时支持纯 POX 编码和 SOAP 编码。使用文本消息编码器可以与非 WCF 终结点交互操作。
 
BinaryMessageEncodingBindingElement
 二进制消息编码器,使用精简二进制格式优化通信, WCF 提供的所有编码器中性能最佳的编码器。
 
MTOMMessageEncodingBindingElement
 绑定元素,指定使用 MTOM 编码的消息的字符编码和消息版本。MTOM 编码以文本形式传输大多数 XML,但是按原样传输较大的二进制数据块。就效率而言, MTOM 介于在文本编码器(最慢)和二进制编码器(最快)之间。
 


    这些是目前我们使用的主要的三种消息编码器类型。但无论什么消息编码器,它们都是MessageEncodingBindingElement 类的子类,必须实现WriteMessage和ReadMessage方法。这两个方法是消息编码的核心实现。《WCF技术内幕》绑定有比较深入的分析。它们的作用如下:

WriteMessage,此方法把Message 对象数据写入到Stream 对象。
ReadMessage,此方法采从Stream 对象读取数据,并构建一个新的Message 对象。
【2】字节流编码:

  WCF4.0提供了一个新的编码器类型:ByteStreamMessageEncodingBindingElement 。它也是MessageEncodingBindingElement的子类型,

public sealed class ByteStreamMessageEncodingBindingElement : MessageEncodingBindingElement
{......}
      但是,没有定义相对应的绑定支持这个编码,因此,如果在WCF4.0中,我们想使用字节流编码机制,必须自己实现自定义绑定。也就是Creating Custom Binding。

【3】自定义绑定:

      这里我们先要了解自定义绑定的概念,然后再来介绍如何通过自定义绑定来使用字节流编码机制。虽然系统提供了许多的绑定,但是WCF框架允许用户自己定义特定的绑定类型。

  我们知道绑定由许多不同的绑定元素(BindingElement)组成,这些具有不同功能的绑定元素(BindingElement)累加起来,组成了一个具有完整功能的绑定(Binding)。

  Binding按照功能划分,可以看到有如下几个主要层次:事务、可靠性、安全性、编码和传输。


 绑定元素
 必需
 
事务
 TransactionFlowBindingElement
 否
 
可靠性
 ReliableSessionBindingElement
 否
 
安全性
 SecurityBindingElement
 否
 
编码
 文本、二进制、消息传输优化机制 (MTOM)、字节流、自定义
 是
 
传输
 TCP、HTTP、HTTPS、命名管道(也称为 IPC)、对等 (P2P)、消息队列(也称为 MSMQ)、自定义
 是
 


  这里只有编码和传输层元素是必须的,其它的根据绑定要实现的功能来选择是否添加进来。我们可以查看一下常见绑定的绑定元素(BindingElement)组成。例如BasicHttpBinding和NetTcpBinding。它们的绑定元素构造如下:

BasicHttpBinding的元素列表:1)   TextMessageEncodingBindingElement2)   HttpTransportBindingElementNetTcpBinding的元素列表:1)   TransactionFlowBindingElement2)   ReliableSessionBindingElement3)   SymmetricSecurityBindingElement4)   BinaryMessageEncodingBindingElement5)   TcpTransportBindingElement 我们能看到为什么BasicHttpBinding的功能如此简单,并且支持Text编码的。而NetTcpBinding相比起来功能更加复杂。比如支持事务、可靠性会话、安全等特性的。这个在《WCF技术内幕》绑定一章里做过详细的介绍。

【4】自定义绑定实现简化字节流编码:

    下面我们来讲解一下代码的实现过程,这个实现的过程有点复杂。因为要通过自定义绑定来使用ByteStream编码,还要注意一些限制,必须只能使用http传输。另外还要使用消息契约MessageContract。过程就有些复杂,可能在实际的项目里,使用的时候,大家都会考虑这个问题。只能等WCF在下一个版本里给出正式的支持在使用,也许会简单很多,也许WCF5.0里会出现一个新的ByteStreamHttpBinding。这样开发的时候就会简单很多。目前我们只能通过自己的代码实现。下面我们来分布介绍实现的过程。

【4.1】服务契约:

      服务契约里我们定义了一个操作为UploadImage。代码如下:

    //1.服务契约
    [ServiceContract]
    public interface IWCFService
    {
        //操作契约
        [OperationContract(Action = "*", ReplyAction = "*")]
        Message UploadImage(Message request);
    }
       服务实现的这个操作,它会再把原始的图片返回给客户端。通过Message来完成。接受客户端消息,并返回消息给客户端,消息体里就是图片的数据。也就是放在<Binary>图片数据</Binary>节点里的。代码如下:

    //2.服务类,继承接口。实现服务契约定义的操作
    public class WCFService : IWCFService
    {
        //实现接口定义的方法
        public Message ProcessRequest(Message request)
        {
            Console.WriteLine("接受图片数据");
            //设置返回消息的属性
            HttpResponseMessageProperty httpResponseProperty = new HttpResponseMessageProperty();
            httpResponseProperty.Headers.Add("Content-Type", "application/octet-stream");
            request.Properties.Add(HttpResponseMessageProperty.Name, httpResponseProperty);
            //打印时间
            Console.WriteLine("返回图片数据 {0}", DateTime.Now.ToLongTimeString());
            return request;
        }
    }
     这里的代码很简单,就是对请求消息做了一下简单地转换,返回给客户单,我们只监控消息的返回时间。

【4.2】自定义绑定:

       这里我们就假定这个绑定的名字为ByteStreamHttpBinding。这个绑定只有两个绑定元素,一个是负责编码的ByteStreamMessageEncodingBindingElement ,另外一个就是负责传输的HttpTransportBindingElement。但是要设置一些属性。比如最大传输消息大小等等。代码如下:

             // Create a custom binding containing two binding elements
             //创建自定义绑定,只能使用HttpTransportBindingElement,并且MessageVersion为None
             ByteStreamMessageEncodingBindingElement byteStreamBindingElement = new ByteStreamMessageEncodingBindingElement();
             byteStreamBindingElement.ReaderQuotas.MaxArrayLength = 900000;
             byteStreamBindingElement.ReaderQuotas.MaxBytesPerRead = 4096;
             byteStreamBindingElement.ReaderQuotas.MaxDepth = 64;
             byteStreamBindingElement.ReaderQuotas.MaxStringContentLength = 900000;
             byteStreamBindingElement.ReaderQuotas.MaxNameTableCharCount = 900000;
             HttpTransportBindingElement transport = new HttpTransportBindingElement();
             transport.TransferMode = TransferMode.Streamed;
             transport.MaxReceivedMessageSize = 900000;
             transport.MaxBufferSize = 900000;
             CustomBinding binding = new CustomBinding(byteStreamBindingElement, transport);
      这个自定义绑定的实现,也可以通过配置文件来完成。另外也可以封装在一个单独的绑定类型ByteStreamHttpBinding。以便重复使用这些代码。

【4.3】宿主:

       宿主使用自定义的绑定来托管WCF服务。代码如下:

   CustomBinding binding = new CustomBinding(byteStreamBindingElement, transport);
            // // Add an endpoint using that binding
            ////使用自定义绑定增加一个终结点
             host.AddServiceEndpoint(typeof(WCFService.IWCFService), binding, "ByteStreamWCFService");
              //判断是否以及打开连接,如果尚未打开,就打开侦听端口
                if (host.State !=CommunicationState.Opening)
                host.Open();
                //显示运行状态
【4.4】客户端:

       客户端这里需要通过Message类型来设置消息。首先我们会从一个犀利哥的图片中读取数据,onst string TestFileName = "./http://www.cnblogs.com/xilige.jpg";这里处理消息体元素生成工作的就是ByteStreamBodyWriter类型,他继承自抽象类BodyWriter,重写了void OnWriteBodyContents(XmlDictionaryWriter writer)方法。这个机制是消息序列化的一个重要步骤。writer会控制每个消息体元素的生成工作。ByteStreamBodyWriter的实现代码如下:

    public class ByteStreamBodyWriter : BodyWriter
    {
        string testFileName;
        public ByteStreamBodyWriter(string testFileName)
            : base(false)
        {
            this.testFileName = testFileName;
        }
        protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
        {
            //生成初始节点<Binary>
            writer.WriteStartElement("Binary");
            //写数据
            FileStream fs = new FileStream(this.testFileName, FileMode.Open);
            byte[] tmp = new byte[fs.Length];
            fs.Read(tmp, 0, tmp.Length);
            writer.WriteBase64(tmp, 0, (int)tmp.Length);
            //生成结束标签</Binary>
            writer.WriteEndElement();
            fs.Close();
        }
    }
      这里客户端代码就是生成一个消息,消息体里读取的是犀利哥的图片,然后使用自定义绑定建立通道发送消息。代码如下:

 const string TestFileName = "./http://www.cnblogs.com/xilige.jpg";
            //ByteStreamBodyWriter writer = new ByteStreamBodyWriter(TestFileName);
            Message message = Message.CreateMessage(MessageVersion.None, "*", new ByteStreamBodyWriter(TestFileName));
            HttpRequestMessageProperty httpRequestProperty = new HttpRequestMessageProperty();
            httpRequestProperty.Headers.Add("Content-Type", "application/octet-stream");
            message.Properties.Add(HttpRequestMessageProperty.Name, httpRequestProperty);
//// Create a channel using that binding
            ////创建通道
            EndpointAddress address = new EndpointAddress("http://localhost:8000/ByteStreamWCFService");
            ChannelFactory<IWCFService> channelFactory = new ChannelFactory<IWCFService>(binding, address);
             IWCFService channel = channelFactory.CreateChannel();
             Console.WriteLine("Client calling service");
             Message result = channel.UploadImage(message);
       基本就是这个过程。

【4.5】运行结果:

       我们可以启动宿主,然后单步执行客户端代码,看看运行的结果,截图如下:


我们看到客户端上传了一个图片,服务端收到以后又返回给客户端,犀利哥的这个照片大小为41.474K。并且数据是放在消息体体的节点<Binary>里。
【5】总结:
     这个新的特性,被称为字节流编码的特性,有点让人无奈,功能是好功能,但是只提供了一个绑定元素,如果我们要使用的话必须自己实现自定义绑定。这个对于大多数开发者来说,可能有点复杂。这里还有几点需要值得注意,就是:
(1)字节流编码ByteStreamMessageEncodingBindingElement目前WCF4.0里只能通过自定义绑定实现。
(2)ByteStreamMessageEncodingBindingElement 只能和Http传输结合,不支持其他的传输。
(3)与流传输一样,字节流不支持可靠性会话,也不支持消息安全。因为数据交大。加密解密处理会耗费较多的资源。
(4)使用Message类型来控制消息的生成过程里,必须把数据放置在节点<Binary></Binary>里。 否则会出现WCF分布式开发常见错误(30):Start element 'Binary' expected(期望的初始元素是'Binary' )错误。
(5)Message类型设置的一个属性"application/octet-stream" ,是是与MIME附件规范,这个表示消息内容是二进制文件。
(6)它与二进制编码器不同的是,二进制编码器是对消息数据做精简二进制编码,而字节流编码则不同,可以传递最原始的二进制数据。
ByteStreamEncoder.rar
(责任编辑:admin)
Tags:WCF4.0
责任编辑:admin
返回顶部
------分隔线----------------------------
推荐内容
骆驼户外男 真皮磨砂日常休闲鞋 低帮 2011秋冬新款 专柜正品特价 骆驼户外男 真皮磨砂日常休闲鞋 低帮 2011秋冬新款 专柜正品特价