WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(下篇)
原文:WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(下篇)
[爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道《天天山海经》为此录制的节目视频(苏州话)]]通过上篇的介绍,我们知道了WCF所有与编码与解码相关的功能都实现在相应的System.Xml.XmlDictionaryWriter和System.Xml.XmlDictionaryReader中。但是在真正的WCF处理框架中,却并不直接使用XmlDictioanryWriter和XmlDictionaryReader对象,而通过相应的消息编码器(System.ServiceModel.Channels.MessageEncoder)对其进行进一步封装,专门用于消息的编码和解码。
一、消息编码器(MessageEncoder)
消息编码器通过类型MessageEncoder表示,MessageEncoder是定义在System.ServiceModel.Channels命名空间下的一个抽象类。从下面的定义中可以看出,MessageEncoder主要包含两种类型的操作:读消息和写消息,分别通过ReaderMessage和WriteMessage方法实现。此外,两个额外的方法,GetProperty<T>用于获取MessageEncoder相关的一些属性,IsContentTypeSupported用于判断MessageEncoder是否支持某种类型的MIME类型。
1: public abstract class MessageEncoder
2: {
3: //其他成员
4: public virtual T GetProperty<T>() where T : class;
5: public virtual bool IsContentTypeSupported(string contentType);
6:
7: public Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager);
8: public Message ReadMessage(Stream stream, int maxSizeOfHeaders);
9: public abstract Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType);
10: public abstract Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType);
11:
12: public abstract void WriteMessage(Message message, Stream stream);
13: public ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager);
14: public abstract ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset);
15:
16: public abstract string ContentType { get; }
17: public abstract string MediaType { get; }
18: public abstract MessageVersion MessageVersion { get; }
19: }
与上面介绍的3种类型的XmlDictionaryWriter/XmlDictionaryReader相对应,WCF同样定义了MessageEncoder:TextMessageEncoder、BinaryMessageEncoder和MtomMessageEncoder三种MessageEncoder,它们分别封装了XmlUTF8TextWriter/XmlUTF8TextReader、XmlBinaryWriter/XmlBinaryReader和XmlMtomWriter/XmlMtomReader。WCF定义了3个相应的工厂类:TextMessageEncoderFactory、BinaryMessageEncoderFactory和MtomMessageEncoderFactory用于创建相应的MessageEncoder。它们共同继承一个抽象类:System.ServiceModel.Channels.MessageEncoderFactory。通过只读属性Encoder得到相应的MessageEncoder。
1: public abstract class MessageEncoderFactory
2: {
3: //其他成员
4: public abstract MessageEncoder Encoder { get; }
5: }
二、 实例演示通过MessageCoder对消息进行编码
接下来,我们来演示一个实例:如何通过MessageCoder对一个具体的Message对象进行编码。本例主要演示TextMessageCoder和MtomMessageEncoder编码方式的对比。此外,为了演示MTOM对二进制数据的编码优化,我们创建一个基于二进制内容的Message对象,并将一个位图作为消息的主体。
我们先创建如下一个静态辅助方法WriteMessage,该方法通过MessageEncoderFactory得到的MessageEncoder对象将Message对象写入一个文件中。
1: static void WriteMessage(MessageEncoderFactory encoderFactory, Message message, string fileName)
2: {
3: using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Write))
4: {
5: encoderFactory.Encoder.WriteMessage(message, stream);
6: }
7: }
如果调用上面的方法,首先需要创建MessageEncoderFactory对象。由于TextMessageEncoderFactory和MtomMessageEncoderFactory是一个内部类型,不能直接实例化,所以只能通过反射的机制创建两个MessageEncoder。下面是TextMessageEncoder和MtomMessageEncoderFactory构造函数的定义。
1: internal class TextMessageEncoderFactory : MessageEncoderFactory
2: {
3: //其他成员
4: public TextMessageEncoderFactory(MessageVersion version, Encoding writeEncoding, int maxReadPoolSize, int maxWritePoolSize, XmlDictionaryReaderQuotas quotas);
5: }
6: internal class MtomMessageEncoderFactory : MessageEncoderFactory
7: {
8: //其他成员
9: public MtomMessageEncoderFactory(MessageVersion version, Encoding writeEncoding, int maxReadPoolSize, int maxWritePoolSize, int maxBufferSize, XmlDictionaryReaderQuotas quotas);
10: }
在下面的代码中,先通过Message的静态方法CreateMessage创建Message对象,需要注意的第3个参数是一个表示位图的Bitmap对象。然后通过反射创建TextMessageEncoderFactory和MtomMessageEncoderFactory对象,并调用上面定义的辅助方法WriteMessage。
1: Message message = Message.CreateMessage(MessageVersion.Default, "http://www.artech.com/myaction", new Bitmap(@"C:\Users\Jinnan\Pictures\photo.jpg"));
2: MessageBuffer buffer = message.CreateBufferedCopy(int.MaxValue);
3:
4: //通过反射创建TextMessageEncoderFactory
5: string encoderFactoryType = "System.ServiceModel.Channels.TextMessageEncoderFactory,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
6: MessageEncoderFactory encoderFactory = (MessageEncoderFactory)Activator.CreateInstance(Type.GetType(encoderFactoryType), MessageVersion.Default, Encoding.UTF8, int.MaxValue, int.MaxValue, new XmlDictionaryReaderQuotas());
7:
8: WriteMessage(encoderFactory, buffer.CreateMessage(), @"E:\message.text.xml");
9:
10: //通过反射创建MtomMessageEncoderFactory
11: encoderFactoryType = "System.ServiceModel.Channels.MtomMessageEncoderFactory,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
12: encoderFactory = (MessageEncoderFactory)Activator.CreateInstance(Type.GetType(encoderFactoryType), MessageVersion.Default, Encoding.UTF8, int.MaxValue, int.MaxValue, int.MaxValue, new XmlDictionaryReaderQuotas());
13:
14: WriteMessage(encoderFactory, buffer.CreateMessage(), @"E:\message.mtom.xml");
下面给出的两段文字分别是通过TextMessageEncoder和MtomMessageEncoder对相同的Message对象进行编码后的结果。从中我们可以清晰地看出,TextMessageEncoder将位图进行Base64编码,编码后的内容以内联(Inline)的方式包含在SOAP主体中。而MtomMessageEncoder会生成一个MIME Multipart/Related XOP Package,SOAP封套作为其主体。编码后的字节和SOAP封套是分离的,SOAP的主体部分并不包含位图的内容,仅仅是通过Context-ID对分离的内容进行引用。
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action s:mustUnderstand="1">http://www.artech.com/myaction</a:Action></s:Header><s:Body><Bitmap xmlns="http://schemas.datacontract.org/2004/07/System.Drawing" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema"><Data i:type="x:base64Binary" xmlns="">/9j/4AAQSkZJRgABAQAAAQABAAD/(...省略...)ZIz7V3gVcR/KPu+lUNWVfs3Qf6309jTkk47DW5/9k=</Data></Bitmap></s:Body></s:Envelope>
Content-Type: multipart/related;type="application/xop+xml";boundary="06a0ac15-70c6-47e9-8837-ebc04a9ac1c2+id=1";start="<http://tempuri.org/0/633655837835941838>";start-info="application/soap+xml"
--06a0ac15-70c6-47e9-8837-ebc04a9ac1c2+id=1
Content-ID: <http://tempuri.org/0/633655837835941838>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml"
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action s:mustUnderstand="1">http://www.artech.com/myaction</a:Action></s:Header><s:Body><Bitmap xmlns="http://schemas.datacontract.org/2004/07/System.Drawing" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema"><Data i:type="x:base64Binary" xmlns=""><xop:Include href="cid:http%3A%2F%2Ftempuri.org%2F1%2F633655837836161838" xmlns:xop="http://www.w3.org/2004/08/xop/include"/></Data></Bitmap></s:Body></s:Envelope>
--06a0ac15-70c6-47e9-8837-ebc04a9ac1c2+id=1
Content-ID: <http://tempuri.org/1/633655837836161838>
Content-Transfer-Encoding: binary
Content-Type: application/octet-stream
[省略不可读的编码内容]
--06a0ac15-70c6-47e9-8837-ebc04a9ac1c2+id=1--
三、WCF体系下的编码机制实现
最后我们来介绍WCF体系下是如何对消息进行编码的。在客户端,以方法调用形式体现的服务访问通过ClientMessageFormatter生成请求消息。该请求消息最终通过绑定对象从服务模型层转到信道层。我们说绑定是绑定元素的有序组合,对于所有类型的绑定来说,有两个绑定类型是必不可少的:MessageEncodingBindingElement和TransportBindingElement。而消息的编码由这两个绑定元素共同完成。
上面我们介绍了3种编码方式:Text、Binary和MTOM;对应3种不同的XmlDictionaryWriter/XmlDictionaryReader:XmlUTF8TextWriter/ XmlUTF8TextReader、XmlBinaryWriter/XmlBinaryReader和XmlMtomWriter/XmlMtomReader;3种XmlDictionaryWriter/XmlDictionaryReader又对应着3种MessageEncoder:TextMessageEncoder、BinaryMessagEncoder和MtomMessageEncoder;这3种不同的MessageEncoder又具有它们各自的MessageEncoderFactory:TextMessageEncoderFactory、BinaryMessagEncoderFactory和MtomMessageEncoderFactory。最终这3种MessageEncoderFactory被3种相应的MessageEncodingBindingElement用于进行具体的编码。MessageEncodingBindingElement通过CreateMessageEncoderFactory得到相应的MessageEncoderFactory。
public abstract class MessageEncodingBindingElement : BindingElement
{
//其他成员
public abstract MessageEncoderFactory CreateMessageEncoderFactory();
public override T GetProperty<T>(BindingContext context) where T: class;
public abstract MessageVersion MessageVersion { get; set; }
}
对应着3种不同的MessageEncoderFactory,WCF定义了3种不同的MessageEncodingBindingElement,它们分别是:TextMessageEncodingBindingElement、BinaryMessageEncodingBindingElement和MtomMessageEncodingBindingElement。
在介绍绑定的时候,我们说BindingElement创建相应的ChannelFactory/ChannelListener,而ChannelFactory/ChannelListener最终创建相应的Channel进行消息的处理。这种说法是不准确的,并不是所有的BindingElement都会创建Channel,实际上没有用于专门编码的Channel,具体的编码工作是TransportChannel完成的。图1揭示了WCF进行消息编码的本质。
图1 WCF体系下消息编码的实现
当通过绑定对象创建信道栈的时候,MessageEncodingBindingElement的BuildChannelFactory/BuildChannelListener方法首先被调用,MessageEncodingBindingElement会创建相应的MessageEncoderFactory对象,将其置于当前的BindingContext中。然后TransportBindingElement的BuildChannelFactory/BuildChannelListener方法被调用,并创建TransportChannelFactory/TransportChannelListener对象,TransportChannelListener和TransportChannelFactory创建TransportChannel用于请求监听和消息发送,与此同时TransportChannel会将MessageEncoderFactory从BindingContext获取下来用于消息的解码和编码。
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(下篇)的更多相关文章
- spring boot / cloud (十九) 并发消费消息,如何保证入库的数据是最新的?
spring boot / cloud (十九) 并发消费消息,如何保证入库的数据是最新的? 消息中间件在解决异步处理,模块间解耦和,和高流量场景的削峰,等情况下有着很广泛的应用 . 本文将跟大家一起 ...
- Objective-C(十九、通知-消息发送模式之中的一个)——iOS开发基础
结合之前的学习笔记以及參考<Objective-C编程全解(第三版)>,对Objective-C知识点进行梳理总结. 知识点一直在变.仅仅是作为參考.以苹果官方文档为准~ 十九.通知-消息 ...
- WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(二)实现IRequestChannel(2016-03-15 12:35)
这是这个系列的第二篇,其他的文章请点击下列目录 WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(一)概要设计 WCF扩展之实现ZeroMQ绑定和protocolBuffer消息 ...
- WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(三)实现ReplyChannel(2016-03-15 12:35)
这是这个系列的第三篇,其他的文章请点击下列目录 WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(一)概要设计 WCF扩展之实现ZeroMQ绑定和protocolBuffer消息 ...
- Java反射机制剖析(四)-深度剖析动态代理原理及总结
动态代理类原理(示例代码参见java反射机制剖析(三)) a) 理解上面的动态代理示例流程 a) 理解上面的动态代理示例流程 b) 代理接口实现类源代码剖析 咱们一起来剖析一下代理实现类($Pr ...
- WCF扩展之实现ZeroMQ绑定和protocolBuffer消息编码(一)概要设计
在我工作的项目中含有多种操作系统.多种设备.多种开发语言,因此需要使用跨平台的通信技术和自定义的消息编码.经过技术调研,ZeroMQ+ProtocolBuffer最终成为通信技术和编码方式.但是如 ...
- leecode第八十九题(格雷编码)
class Solution { public: vector<int> grayCode(int n) { vector<int> res; res.push_back(); ...
- WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]
原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码 ...
- WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用
原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...
随机推荐
- Regex阅读笔记(二)之环视
环视不匹配任何字符,只匹配文本中的特定位置. 正序环视:(?=) 逆序环视:(?<=) 非捕获(?:) 环视会检查子表达式能否匹配,但它只寻找能够匹配的位置,而不会真正占用这些字符. -用在字符 ...
- HTTPS的学习总结
HTTPS学习总结 简述 HTTPS对比HTTP就多了一个安全层SSL/TLS,具体就是验证服务端的证书和对内容进行加密. 先来看看HTTP和HTTPS的区别 我用AFN访问http下的httpbin ...
- 转: c++继承中的内存布局
英文原文: http://www.openrce.org/articles/files/jangrayhood.pdf 翻译: http://blog.csdn.net/jiangyi711/arti ...
- VC问题 IntelliSense:“没有可用的附加信息”,[请参见“C++项目 IntelliSense 疑难解答”,获得进一步的帮助]
当出现以上的问题时,若按照网上所说的解决方法: 1.安装VA(Visual_AssistX) 2.安装Microsoft SQL Server Compact 3.5 3.更改设置“工具-选项-文本编 ...
- css3.0
css3.0相比css2.0多了些我们经常需要使用的标签属性,例如:圆角,个别圆角,不透明度,阴影特效等 1.圆角(即如何画圆)border-radius a{width:20px; height:2 ...
- iOS开发进阶之 UIWebView
刚接触IOS开发1年多,现在对于混合式移动端开发越来越流行,因为开发成本上.速度上都比传统的APP开发要好,混合式开发是传统模式与PC网页端相结合的模式.那么提到了 APP的混合模式开发,在Andro ...
- [Swust OJ 1091]--土豪我们做朋友吧(并查集,最值维护)
题目链接:http://acm.swust.edu.cn/problem/1091/ Time limit(ms): 1000 Memory limit(kb): 32768 人都有缺钱的时候,缺 ...
- media_root以及static_root配置
# At the top of settings/base.pyfrom os.path import join, abspath, dirnamehere = lambda *x: join(abs ...
- Windows服务编程集合
http://zyan.cc/windows_mstsc/ Optionname--Optionvalues描述 type=----own, share, interact, kernel, file ...
- Uniconnection 连 mysql 有时会断线的
你定义localfailover:=ture.断线后会自己接上 firedac没这种功能.只有unidac有