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

来源:互联网 发布:centos 7 装禅道 编辑:程序博客网 时间:2024/06/09 22:56
Posted on 2010-03-28 22:37 Frank Xu Lei 阅读(1379) 评论(8)  编辑 收藏 所属分类: SOA and EAI, WCF4.0新特性体验

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

【1】WCF编码器:

 我们知道在WCF4.0以前的版本中已经提供了三种编码器:TextBinaryMTOM 消息编码器。当然我们也可以实现自定义编码器。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)、字节流、自定义

传输

TCPHTTPHTTPS、命名管道(也称为 IPC)、对等 (P2P)、消息队列(也称为 MSMQ)、自定义

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

BasicHttpBinding的元素列表:
1)   TextMessageEncodingBindingElement
2)   HttpTransportBindingElement
NetTcpBinding的元素列表:
1)   TransactionFlowBindingElement
2)   ReliableSessionBindingElement
3)   SymmetricSecurityBindingElement
4)   BinaryMessageEncodingBindingElement
5)   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)它与二进制编码器不同的是,二进制编码器是对消息数据做精简二进制编码,而字节流编码则不同,可以传递最原始的二进制数据。

    最后给出本文的例子代码给大家学习的时候做个参考。/Files/frank_xl/9.ByteStreamEncoder.rar欢迎留言交流。

 参考资料:

1.http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.bytestreammessageencodingbindingelement(VS.100).aspx

2.http://msdn.microsoft.com/zh-cn/library/ms735115.aspx

3.http://msdn.microsoft.com/zh-cn/library/aa347793.aspx

4.http://msdn.microsoft.com/zh-cn/library/aa751889.aspx

5.《WCF技术内幕》:绑定

6.http://kb.iu.edu/data/agtj.html

 


老徐的博客
【作      者】:Frank Xu Lei
【地      址】:http://www.cnblogs.com/frank_xl/
【中文论坛】:微软WCF中文技术论坛
【英文论坛】:微软WCF英文技术论坛

原创粉丝点击