为WCF增加UDP绑定(储备篇)
日前我开发的服装DRP需要用到即时通信方面的技术,比如当下级店铺开出零售单时上级机构能实时收到XX店铺XX时XX分卖出XX款衣服X件之类的信息,当然在上级发货时,店铺里也能收到已经发货的提醒。即时通信技术能运用到DRP系统的很多方面,若深入下去,甚至可以开发一个系统内部的通讯模块,类似于QQ。当前大部分的企业管理系统开发类似功能,使用的都是效率及其低下的定时拉数据的方式,即每隔一段预定时间去数据存储区(一般为数据库)读取数据,然后在代码层判断,若数据同内存数据一致则丢弃,否则更新内存数据并同步更新UI。这种方式的缺点显而易见:效率低;服务器压力大;数据实时性不强。因此我花时间了解了我可能用到的关于真正意义上的即时通信技术的一些知识,作为开发之前的知识储备。有关这个功能的开发我会详细记录日志供其他朋友和自己以后参考学习。
-
P2P传输协议选择:
一般集中在TCP和UDP上,这两种各有优劣,说简单点就是TCP安全稳健效率低,而UDP效率高但可能会丢三落四(视网络情况定,丢包的概率不高)。说些题外话,有时写代码写着写着就会想到哲学方面的东西,比如TCP和UDP的比较会发现,效率高且安全可靠的传输协议并不存在,这种情况屡见不鲜,所谓鱼和熊掌不可兼得,也许这就是宇宙的法则吧。我选择UDP,首先UDP丢包的概率不高,其次若选择TCP还要考虑连接数啥啥的问题,不胜烦恼。
-
“打洞”
这个词让人浮想联翩,对搞网络通信的人来说却毫无吸引力。这种体力活源自IP地址太少,各种NAT设备(俗称路由器)应运而生,解决了僧多粥少的问题,但引入了另一个问题,即任意连网的电脑并不能确保可以直接通信,假如它们隐藏在各自的NAT之后,并没有确切的网络地址,地址都不知道,谈何通信。虽然隐藏在NAT后面电脑没有公网地址,但NAT知道哪些消息是发给谁并知道怎么把这些消息送达,前提是这个消息是受信任的。
“打洞”(我不知道为什么要叫这个名词)简单的说就是在两端NAT建立起信任的连接。这个过程首先需要一台拥有公网地址的机器S作为媒介,现在假设处于某个局域网内部的机器A想同另一个局域网内的机器B通信,那么如下操作就是一次“打洞”:
-
A连接S获取B的公网地址(通过B局域网的NAT对外公布)
-
A按照这个地址向B随便发一点东西(东西不重要,目的是让对方对你有一定的印象),A的NAT将B地址列入信任列表
-
毫无疑问,B的NAT把关很严,这种套近乎的方式他见得多了,大部分是图谋不轨,因此他并未理会,也未告知B这个事情。
-
A告知S他想认识B,希望S帮忙撮合
-
S当然是B的NAT熟悉的人,这个媒人已经帮B促成千万次的约会了,因此S被准许将A的意图告知B,并给了A的公网地址(通过A的NAT对外公布),B答应考虑A的请求
-
若B对A有意,那么她可以按照A的地址随便发一点东西过去,此时B的NAT将A地址列入信任列表。此时“打洞”完毕。
少年们于是不淡定了,啥都没有发生呢,怎么就完了呢。其实“打洞”过程就是相互建立信任的过程,相互加入各自的信任列表之后,以后就能按照各自NAT的公网地址进行直连通信了,这又是另外的故事了。上述过程对少数NAT设备来说可能并不准确。
UDP组播通信似乎用不着“打洞”(假如路由器支持IGMP协议的话),不知道我的这个理解有没有问题,待以后验证。
-
为WCF增加UDP绑定
我的服装DRP采用WCF作为服务提供者。本来我想绕过WCF,直接操作Socket来进行UDP通信。后来想说顺便熟悉下WCF这个框架的底层机制,于是下载了微软的一个示例,并研读了蒋大牛的《WCF技术剖析》,这本书写的非常好。两者结合,许多困惑之处往往能柳暗花明。现将这两天的学习做一总结。(听说WCF4.5加入了UDP绑定,具体看What’s new in WCF 4.5? UDP transport support)
-
一个绑定由若干绑定元素构成,每个绑定元素负责客户端和服务器端的信道管理器ChannelManager的创建,信道管理器在客户端和服务器端有不同的名称,前者为信道工厂ChannelFactory,后者为信道监听器ChannelListener,他俩有个共同的责任就是创建各自对应(即在客户端和服务器端分别创建)的信道。一个信道管理器创建相应类型的信道(由于一般对同一个连接来说,一个类型在所在的信道栈里只有一个的信道,因此我们可以说信道管理器和信道一一对应,有信道栈,即有信道管理器栈)。一个信道有所谓的“信道形状”,表示它是什么类型的信道,即输出、输入、请求、回复还是双工。另外信道从会话角度还分为数据包信道(笔者注:该名词取自《WCF技术剖析》一书,但个人觉得应该是非会话信道更准确,因为数据包和传输信道而非所有种类信道更相关,而任何传输信道传送的都是数据包)和会话信道(此处所谓的会话包括协议本身的会话机制如tcp协议,也包括WCF框架提供的会话机制),很明显,UDP使用的就应该是数据包信道(按前面所述,这句话虽然无错,但也无意义。任何传输信道都可以附加会话机制以实现相关消息的来回传送)。
-
绑定元素构成绑定的次序也很重要,按上一条所述可得,绑定元素的次序决定了信道的次序,而信道的次序本质上决定了绑定的特性与能力。消息在信道栈中应该是自上而下逐信道处理的。
-
WCF的终结点三要素中,我们可以只通过绑定和终结点地址进行客户端和服务器端的[消息]通信,服务接口要素只是借助通信的功能调用服务端的方法并返回而已(这句话并不确切,确切地说,服务契约中的操作契约本质上定义了操作(而非信道)采用的消息交换模式,以及消息的格式,这个作用主要在发布元数据生成WSDL时用到。)。
-
终结点有逻辑地址和物理地址之分,一般说的终结点地址是逻辑地址,我个人理解逻辑地址就是SOAP的To,可以指定物理地址从而忽略逻辑地址,否则WCF会有自己的一套映射机制从逻辑地址得到物理地址。最终的调用地址是物理地址。 假设我们为一个服务(同一个服务接口)指定了3个终结点地址(逻辑地址),其中两个终结点地址分配同一个监听地址(物理地址),此时有两个监听地址。那么当服务被成功寄宿时,WCF会创建两个信道分发器ChannelDispatcher对象,每个信道分发器各拥有属于自己的信道监听器对象,它们分别绑定到两个监听地址进行服务调用请求的监听。此外,WCF还会为3个终结点地址创建3个终结点分发器EndpointDispatcher。当信道分发器通过信道监听器接收到消息时,将会根据消息自身携带的信息按照终结点分发器的指定规则选择与之相匹配的终结点分发器,这一过程称为消息筛选MessageFilter。
-
操作约束OperationContract指定的Action应该对应的是SOAP的Action,作为上述的筛选规则供信道分发器将消息路由到终结点分发器。
-
在WCF中有一种服务操作能处理所有的消息类型,叫做非匹配处理器,它接受一个Message类型的参数(因为所有的服务调用最终都转化为Message抵达服务器),返回Message或void,且Action被指定为“*”。对一个服务契约来说,这样的操作契约只能有一个。同理,ReplyAction=”*” 可以使用一个Operation处理所有的返回消息。
-
WCF终结点信息最终要被转化为WSDL方才能被客户端理解调用,这涉及到WsdlExporter这个类,具体可看WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[实现篇]。为了让我们自定义的绑定元素被WsdlExporter导出为对应的WSDL元素,我们需要让自定义绑定元素实现WSDL导出扩展(WSDL Export Extension)和策略导出扩展(Policy Export Extension)。关于策略一说可阅读Understanding Web Services Policy。简单地说,策略描述的是服务的能力和要求(目前我只看到要求),假如没有导出相关策略,客户端就不知道这一部分的具体要求,可能会导致请求得不到服务的正确响应。因此,策略导出是自定义绑定的一个职责。策略属于WSDL的一部分。关于策略导出请参看Extending WSDL and Policy, Part 1. Exporting custom policy assertions。有导出就有导入,导入是在客户端进行的。对于自定义策略来说,我们要实现IPolicyImportExtension接口,以便获取导出的策略并将其按照预期应用到绑定或其它地方,具体步骤请参看IPolicyImportExtension接口。
-
补充第7条:WSDL元数据扩展(自定义)导入(包括自定义策略导入),是在客户端通过客户端配置文件或Svcutil.exe配置文件或通过编程方式将其添加到System.ServiceModel.Description.WsdlImporter构造函数加载进行的。
下一篇我会参照上面给出的微软的那个示例,在WCF下实现UDP通信,并增加P2P功能。
ps:这篇是从百度空间搬过来的,由于百度喜欢和谐,导致很多超链接丢失,偶尔还有丢失语句的情况。我粗粗查看了一遍,修复了几处。关于丢失的链接,若读者有兴趣则可根据文本谷歌,应该能找到对应的资料。