深入剖析WCF的可靠会话[实例篇](内含美女图片,定力差者慎入)
2011-09-15 12:13
309 查看
作为一个通信基础平台,WCF必须保证通信的可靠性。由于消息交换是WCF采用的通信手段,通信可靠性的保障体现在确保消息的可靠传输。WCF本质上是一个消息处理框架,作为整个消息交换系统的两个终端,即发送端和接收端。换句话说,WCF仅仅负责对消息的发送和接收,一旦消息通过WCF的信道层进入了网络,就脱离了WCF的控制范围。但是,由于网络环境的限制,网络层不能百分之百地确保对消息的有效交付。如何克服中间环节的制约,确保从一端发送的消息能够被有效地交付给另一端,这就是可靠消息传输(ReliableMessaging)需要解决的问题。WCF通过可靠会话(ReliableSessions)实现了种种端到端(EndtoEnd)的可靠消息传输。源代码从这里下载。
为了让读者对可靠会话的作用现有一个直观的认识,我们先来做一个很有意思的实例演示。接下来我们将要演示的实例是对可靠会话确保WCF消息传输的可靠性的一个直观的反应,也是早年微软推广WCF技术频繁使用的案例:图片传输。在客户端,我们选择一张图片,并对它进行切片,最后通过调用WCF服务将每一个切片依次传输到服务端。服务端则按照切片被接收到的顺序重新组装成一张完整的图片。如果中间有任何一张切片丢失,服务端最终组装图片将不会完整;如果服务端切片接收的次序和发送顺序不一致,将会造成组装后的图片并不能还原其发送前的模样。在这里,我们充分利用了WCF中的可靠会话提供了可靠而有序的消息交付。
不稳定的网络是造成消息丢失最主要的因素,但是在本机环境下模拟不稳定的网络是一件比较困难的事情。但是,虽然我们不能让消息在网络传输层中丢失,但是我们可以让它在WCF的信道层中丢失。如何实现这样的目的呢,相应阅读过《WCF技术剖析(卷1)》第3章的读者会很快想到可以采用自定义信道的方式。
接下来我们就来创建这个用于模拟不稳定网络环境的自定义信道UnreliableNetworkSimulateChannel。由于我们即将演示的实例采用TCP传输方式,所以我们让UnreliableNetworkSimulateChannel实现了IDuplexSessionChannel接口。UnreliableNetworkSimulateChannel通过MessageInspector对象对传入的消息进行加工(根据丢包率随即地丢弃)。MessageInspector在构造函数中创建,而丢包率通过参数传入。除了Send方法,几乎所有的成员都是调用InnerChannel相应的方法或者返回同名的属性。由于在《WCF技术剖析(卷1)》我们有过对如何自定义信道的专门介绍,在这里我们就不在多做重复的讲述了。
通过上面的代码我们可以看到,在Send方法中,消息对象会先传入MessageInspector的ProcessMessage方法中,如果返回值不为空,将其递交给InnerChannel,反之意味着消息在信道层中丢失。接下来我们为该自定义信道创建信道管理器,由于该信道只在客户端使用,我们只需要为之创建信道工厂即可(ChannelFactory)。UnreliableNetworkSimulateChannel对应的信道工厂UnreliableNetworkSimulateChannelFactory<TChannel>定义如下。
由于WCF信道栈的缔造者为绑定,而信道管理器(信道工厂或者信道监听器)最终借助于绑定元素而发送作用。为此,我们为我们创建的信道工厂创建了如下一个绑定元素:UnreliableNetworkSimulateBindingElement。
为了使上面的绑定元素具有可配制性,我们还需要为之创建相应的配置元素。在WCF编程模型下,我们只需要集成BindingElementExtensionElement类即可。在下面定义的UnreliableNetworkSimulateExtensionElement,我们将丢包率定义成配置属性,该属性默认值为20(20%丢包率)。
服务端需要将接收到的图片切片组装成一个完整的图片,我将图片组装的功能通过如下一个叫做ImageAssembler的静态类来提供。对应于服务契约定义的两个服务操作,ImageAssembler中定义两个静态事件ImageSliceReceived和ImageErasing。这两个事件分别通过静态方法ReceiveImageSlice和Erase出发。事件ImageSliceReceived的事件参数类型为ImageReceivedEventArgs,它和ImageAssembler定义如下。
接下来是服务传输服务的实现,该服务定义在如下的ImageTransferService类中。对于两个服务操作,我们分别调用ImageAssembler的两个对应的静态方法提供实现。
通过上面的配置我们会发现,我们为ImageTransferService配置了三个终结点,它们均采用自定义绑定,并且采用TCP传输方式和二进制消息编码。考虑到对较大尺寸图片的支持,我们将BinaryMessageEncodingElement的MaxArrayLength属性,以及TcpTransportElement的MaxBufferSize和MaxReceivedMessageSize都设置成最大。对于这三个终结点的绑定配置,具有如下不一致的地方。reliableSession和orderedDelivery终结点对应的绑定比nonReliableSession多了一个ReliableSessionElement绑定元素。相信你已经猜到了,ReliableSessionElement是为了实现可靠会话而存在的。进一步地,oreliableSession和orderedDelivery终结点绑定的ReliableSessionElement的Ordered属性分别为False和True。也就是意味着orderedDelivery终结点能够实现对消息的有序交付,而reliableSession终结点则不能。
图片的接收窗口如图1所示,其中每一个方格是一个PictureBox,用户显示接收到的图片切片。对于这些PictureBox的ID,从上到下,从左到右依次是pictureBox11、pictureBox12、...、pictureBox15、...、pictureBox55。整个服务寄宿和图片接收实现在如下的代码中。值得注意的一点是,ImageAssembler_ImageCliceReceived方法将接收到的字节数组转化成位图,依次显示到上述的25个PictureBox上。在方法上面应用了一个MethodImplAttribute特性并指定MethodImplOptions.Synchronized作为参数,所以该方法是同步执行的。也就是说,该方法处理的消息次序就是消息被交付的次序。
图1图片接收窗口
同服务寄宿端的配置一样,客户端也配置了三种自定义绑定。所不同的是,它们均多了一个额外的绑定元素UnreliableNetworkSimulateBindingElement,即我们之前创建的用于模拟不稳定网络环境的邦定元素。
图2是图片发送窗口,上边部分一个Picture,会显示通过点击Browse按钮选择的图片。当成功选择某一张用于发送的图片后,点击Send按钮将其发送。ReliableSession和OrderedDelivery两个CheckBox供用户决定是否采用可靠会话,以及有序交付机制进行图片的发送。默认情况下,并不采用可靠会话机制进行图片发送。图片的选择、切片和发送通过下面的代码实现。
通过上面的代码我们可以发现,我们通过GetProxy()方法选择相应终结点对应的ChannelFactory<IImageTransfer>对象,并创建服务代理。在buttonSend_Click方法中,被选择的图片被均分成25个切片,并按照从上到下、从左至右的顺旬转化成字节数据,最终利用创建的服务代理发送出去。在发送之前,调用Erase服务操作通知接收端擦除已经接收到的切片。
图2图片发送端窗口
所有的编程工作完成后,我们来运行我们的程序。图3表示的是没有采用可靠会话时的图片传输情况。从中我们可以看到两接收方组装后的图片不完整,有四个切片缺失。此外,接收方组装后的切片完全是错位的。
图3没有采用可靠会话图片传输情况
图4表示的是选择了可靠会话选项,但是没有选择有序交付选项时图片传输的情况。我们可以看出,这一次解决了切片丢失的问题,但是错位的情况下依然存在。
图4选择可靠会话但不选择有序交付时图片传输情况
最后,我们同是选择可靠会话和有序交付两个选项,你在接收端将会得到一张完完整整地图片,既不会有切片丢失,也不会出现切片错位的情况。最终的结果如图5所示,这才是我们希望的。
图5同时选择可靠会话和有序交付时图片传输情况
实际上,WCF的可靠会话涉及到WS中一个重要的概念——可靠消息传输(RM:ReliableMessaging)。如果想对可靠会话有一个深入的认识,对可靠消息传输的了解是必须的。《概念篇》中将会对RM的基本概念进行基本的讲述。
作者:Artech
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
为了让读者对可靠会话的作用现有一个直观的认识,我们先来做一个很有意思的实例演示。接下来我们将要演示的实例是对可靠会话确保WCF消息传输的可靠性的一个直观的反应,也是早年微软推广WCF技术频繁使用的案例:图片传输。在客户端,我们选择一张图片,并对它进行切片,最后通过调用WCF服务将每一个切片依次传输到服务端。服务端则按照切片被接收到的顺序重新组装成一张完整的图片。如果中间有任何一张切片丢失,服务端最终组装图片将不会完整;如果服务端切片接收的次序和发送顺序不一致,将会造成组装后的图片并不能还原其发送前的模样。在这里,我们充分利用了WCF中的可靠会话提供了可靠而有序的消息交付。
不稳定的网络是造成消息丢失最主要的因素,但是在本机环境下模拟不稳定的网络是一件比较困难的事情。但是,虽然我们不能让消息在网络传输层中丢失,但是我们可以让它在WCF的信道层中丢失。如何实现这样的目的呢,相应阅读过《
<system.serviceModel> <bindings> <customBinding> <bindingname="nonReliableSession"> <binaryMessageEncoding> <readerQuotasmaxArrayLength="2147483647"/> </binaryMessageEncoding> <tcpTransportmaxBufferSize="2147483647"maxReceivedMessageSize="2147483647"/> </binding> <bindingname="reliableSession"> <reliableSessionordered="false"/> <binaryMessageEncoding> <readerQuotasmaxArrayLength="2147483647"/> </binaryMessageEncoding> <tcpTransportmaxBufferSize="2147483647"maxReceivedMessageSize="2147483647"/> </binding> <bindingname="orderedDelivery"> <reliableSessionordered="true"/> <binaryMessageEncoding> <readerQuotasmaxArrayLength="2147483647"/> </binaryMessageEncoding> <tcpTransportmaxBufferSize="2147483647"maxReceivedMessageSize="2147483647"/> </binding> </customBinding> </bindings> <services> <servicename="Artech.ImageTransfer.Service.ImageTransferService"> <endpointaddress="net.tcp://127.0.0.1:7777/imagetransferservice"binding="customBinding"bindingConfiguration="nonReliableSession"contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/> <endpointaddress="net.tcp://127.0.0.1:8888/imagetransferservice"binding="customBinding"bindingConfiguration="reliableSession"contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/> <endpointaddress="net.tcp://127.0.0.1:9999/imagetransferservice"binding="customBinding"bindingConfiguration="orderedDelivery"contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/> </service> </services> </system.serviceModel>
步骤一:通过自定义信道模拟不稳定的网络
为了对网络传输过程中的丢包率能够进行动态控制,我特意创建一个特殊的类型MessageInspector。MessageInspector定义如下,只读属性DropRate表示丢包率,ProcessMessage对传入的消息进行处理,如果返回为Null,意味着消息的丢失。MessageInspector定义如下。usingSystem;
usingSystem.ServiceModel.Channels;
namespaceArtech.ImageTransfer.Extensions
{
publicclassMessageInspector
{
publicintDropRate{get;privateset;}
publicRandomRandomizer{get;privateset;}
publicMessageInspector(intdropRate)
{
this.DropRate=dropRate;
this.Randomizer=newRandom();
}
publicvirtualvoidProcessMessage(refMessagemessage)
{
intrandomNumber=this.Randomizer.Next(100);
if(randomNumber<=this.DropRate)
{
message=null;
}
}
}
}
接下来我们就来创建这个用于模拟不稳定网络环境的自定义信道UnreliableNetworkSimulateChannel。由于我们即将演示的实例采用TCP传输方式,所以我们让UnreliableNetworkSimulateChannel实现了IDuplexSessionChannel接口。UnreliableNetworkSimulateChannel通过MessageInspector对象对传入的消息进行加工(根据丢包率随即地丢弃)。MessageInspector在构造函数中创建,而丢包率通过参数传入。除了Send方法,几乎所有的成员都是调用InnerChannel相应的方法或者返回同名的属性。由于在《
usingSystem;
usingSystem.ServiceModel.Channels;
namespaceArtech.ImageTransfer.Extensions
{
publicclassUnreliableNetworkSimulateChannel:IDuplexSessionChannel
{
publicIDuplexSessionChannelInnerChannel{get;privateset;}
publicMessageInspectorMessageInspector{get;privateset;}
publicUnreliableNetworkSimulateChannel(IDuplexSessionChannelinnerChannel,intdropRate)
{
this.InnerChannel=innerChannel;
this.MessageInspector=newMessageInspector(dropRate);
}
publicIAsyncResultBeginReceive(TimeSpantimeout,AsyncCallbackcallback,objectstate)
{
returnthis.InnerChannel.BeginReceive(timeout,callback,state);
}
publicvoidSend(Messagemessage,TimeSpantimeout)
{
this.MessageInspector.ProcessMessage(refmessage);
if(null!=message)
{
this.InnerChannel.Send(message,timeout);
}
}
publicvoidSend(Messagemessage)
{
this.MessageInspector.ProcessMessage(refmessage);
if(null!=message)
{
this.InnerChannel.Send(message);
}
}
//其他成员:直接调用InnerChannel的相应的方法或者返回同名属性
}
}
通过上面的代码我们可以看到,在Send方法中,消息对象会先传入MessageInspector的ProcessMessage方法中,如果返回值不为空,将其递交给InnerChannel,反之意味着消息在信道层中丢失。接下来我们为该自定义信道创建信道管理器,由于该信道只在客户端使用,我们只需要为之创建信道工厂即可(ChannelFactory)。UnreliableNetworkSimulateChannel对应的信道工厂UnreliableNetworkSimulateChannelFactory<TChannel>定义如下。
usingSystem;
usingSystem.ServiceModel.Channels;
usingSystem.ServiceModel;
namespaceArtech.ImageTransfer.Extensions
{
publicclassUnreliableNetworkSimulateChannelFactory<TChannel>:ChannelFactoryBase<IDuplexSessionChannel>
{
publicintDropRate{get;privateset;}
publicIChannelFactory<TChannel>InnerChannelFactory{get;privateset;}
publicUnreliableNetworkSimulateChannelFactory(BindingContextcontext,intdropRate):base(context.Binding)
{
this.InnerChannelFactory=context.BuildInnerChannelFactory<TChannel>();
this.DropRate=dropRate;
}
protectedoverrideIDuplexSessionChannelOnCreateChannel(EndpointAddressaddress,Urivia)
{
varinnerChannel=(IDuplexSessionChannel)this.InnerChannelFactory.CreateChannel(address,via);
returnnewUnreliableNetworkSimulateChannel(innerChannel,this.DropRate);
}
protectedoverrideIAsyncResultOnBeginOpen(TimeSpantimeout,AsyncCallbackcallback,objectstate)
{
returnthis.InnerChannelFactory.BeginOpen(timeout,callback,state);
}
protectedoverridevoidOnEndOpen(IAsyncResultresult)
{
this.InnerChannelFactory.EndOpen(result);
}
protectedoverridevoidOnOpen(TimeSpantimeout)
{
this.InnerChannelFactory.Open(timeout);
}
}
}
由于WCF信道栈的缔造者为绑定,而信道管理器(信道工厂或者信道监听器)最终借助于绑定元素而发送作用。为此,我们为我们创建的信道工厂创建了如下一个绑定元素:UnreliableNetworkSimulateBindingElement。
usingSystem.ServiceModel.Channels;
namespaceArtech.ImageTransfer.Extensions
{
publicclassUnreliableNetworkSimulateBindingElement:BindingElement
{
publicintDropRate{get;set;}
publicUnreliableNetworkSimulateBindingElement(intdropRate)
{
this.DropRate=dropRate;
}
publicoverrideBindingElementClone()
{
returnnewUnreliableNetworkSimulateBindingElement(this.DropRate);
}
publicoverrideTGetProperty<T>(BindingContextcontext)
{
returncontext.GetInnerProperty<T>();
}
publicoverrideIChannelFactory<TChannel>BuildChannelFactory<TChannel>(BindingContextcontext)
{
return(IChannelFactory<TChannel>)newUnreliableNetworkSimulateChannelFactory<TChannel>(context,this.DropRate);
}
}
}
为了使上面的绑定元素具有可配制性,我们还需要为之创建相应的配置元素。在WCF编程模型下,我们只需要集成BindingElementExtensionElement类即可。在下面定义的UnreliableNetworkSimulateExtensionElement,我们将丢包率定义成配置属性,该属性默认值为20(20%丢包率)。
usingSystem;
usingSystem.Configuration;
usingSystem.ServiceModel.Channels;
usingSystem.ServiceModel.Configuration;
namespaceArtech.ImageTransfer.Extensions
{
publicclassUnreliableNetworkSimulateExtensionElement:BindingElementExtensionElement
{
[ConfigurationProperty("dropRate",IsRequired=false,DefaultValue=20)]
publicintDropRate
{
get
{
return(int)this["dropRate"];
}
set
{
this["dropRate"]=value;
}
}
publicoverrideTypeBindingElementType
{
get{returntypeof(UnreliableNetworkSimulateBindingElement);}
}
protectedoverrideBindingElementCreateBindingElement()
{
returnnewUnreliableNetworkSimulateBindingElement(this.DropRate);
}
}
}
步骤二:创建图片传输服务
解决了对不稳定网络环境的模拟问题,我们现在正式来创建我们用于图片传输的WCF服务。先来看看服务契约的定义。服务契约IImageTransfer具有两个单向(One-Way)服务操作。Transfer方法用于对图片切片(以字节数组的形式)的传输,而Erase则用于通知接收端将之前接收的图片删除。usingSystem.ServiceModel;
namespaceArtech.ImageTransfer.Service.Interface
{
[ServiceContract(Namespace="http://www.artech.com/")]
publicinterfaceIImageTransfer
{
[OperationContract(IsOneWay=true)]
voidTransfer(byte[]imageSlice);
[OperationContract(IsOneWay=true)]
voidErase();
}
}
服务端需要将接收到的图片切片组装成一个完整的图片,我将图片组装的功能通过如下一个叫做ImageAssembler的静态类来提供。对应于服务契约定义的两个服务操作,ImageAssembler中定义两个静态事件ImageSliceReceived和ImageErasing。这两个事件分别通过静态方法ReceiveImageSlice和Erase出发。事件ImageSliceReceived的事件参数类型为ImageReceivedEventArgs,它和ImageAssembler定义如下。
usingSystem;
namespaceArtech.ImageTransfer.Service
{
publicstaticclassImageAssembler
{
publicstaticvoidReceiveImageSlice(byte[]imageSlice)
{
if(null!=ImageSliceReceived)
{
ImageSliceReceived(null,newImageReceivedEventArgs(imageSlice));
}
}
publicstaticvoidErase()
{
if(null!=ImageErasing)
{
ImageErasing(null,EventArgs.Empty);
}
}
publicstaticeventEventHandler<ImageReceivedEventArgs>ImageSliceReceived;
publicstaticeventEventHandlerImageErasing;
}
publicclassImageReceivedEventArgs:EventArgs
{
publicbyte[]ImageSlice
{get;privateset;}
publicImageReceivedEventArgs(byte[]imageSlice)
{
this.ImageSlice=imageSlice;
}
}
}
接下来是服务传输服务的实现,该服务定义在如下的ImageTransferService类中。对于两个服务操作,我们分别调用ImageAssembler的两个对应的静态方法提供实现。
usingSystem.ServiceModel;
usingArtech.ImageTransfer.Service.Interface;
namespaceArtech.ImageTransfer.Service
{
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
publicclassImageTransferService:IImageTransfer
{
publicvoidTransfer(byte[]imageSlice)
{
ImageAssembler.ReceiveImageSlice(imageSlice);
}
publicvoidErase()
{
ImageAssembler.Erase();
}
}
}
步骤三:服务寄宿和图片接收程序实现
图片传输服务ImageTransferService最终被寄宿于一个WindowsForms应用中,该应用同时作为图片接收程序使用。我们先来看看服务寄宿端的配置:<?xmlversion="1.0"encoding="utf-8"?>
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<bindingname="nonReliableSession">
<binaryMessageEncoding>
<readerQuotasmaxArrayLength="2147483647"/>
</binaryMessageEncoding>
<tcpTransportmaxBufferSize="2147483647"maxReceivedMessageSize="2147483647"/>
</binding>
<bindingname="reliableSession">
<reliableSessionordered="false"/>
<binaryMessageEncoding>
<readerQuotasmaxArrayLength="2147483647"/>
</binaryMessageEncoding>
<tcpTransportmaxBufferSize="2147483647"maxReceivedMessageSize="2147483647"/>
</binding>
<bindingname="orderedDelivery">
<reliableSessionordered="true"/>
<binaryMessageEncoding>
<readerQuotasmaxArrayLength="2147483647"/>
</binaryMessageEncoding>
<tcpTransportmaxBufferSize="2147483647"maxReceivedMessageSize="2147483647"/>
</binding>
</customBinding>
</bindings>
<services>
<servicename="Artech.ImageTransfer.Service.ImageTransferService">
<endpointaddress="net.tcp://127.0.0.1:7777/imagetransferservice"binding="customBinding"bindingConfiguration="nonReliableSession"contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/>
<endpointaddress="net.tcp://127.0.0.1:8888/imagetransferservice"binding="customBinding"bindingConfiguration="reliableSession"contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/>
<endpointaddress="net.tcp://127.0.0.1:9999/imagetransferservice"binding="customBinding"bindingConfiguration="orderedDelivery"contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/>
</service>
</services>
</system.serviceModel>
</configuration>
通过上面的配置我们会发现,我们为ImageTransferService配置了三个终结点,它们均采用自定义绑定,并且采用TCP传输方式和二进制消息编码。考虑到对较大尺寸图片的支持,我们将BinaryMessageEncodingElement的MaxArrayLength属性,以及TcpTransportElement的MaxBufferSize和MaxReceivedMessageSize都设置成最大。对于这三个终结点的绑定配置,具有如下不一致的地方。reliableSession和orderedDelivery终结点对应的绑定比nonReliableSession多了一个ReliableSessionElement绑定元素。相信你已经猜到了,ReliableSessionElement是为了实现可靠会话而存在的。进一步地,oreliableSession和orderedDelivery终结点绑定的ReliableSessionElement的Ordered属性分别为False和True。也就是意味着orderedDelivery终结点能够实现对消息的有序交付,而reliableSession终结点则不能。
图片的接收窗口如图1所示,其中每一个方格是一个PictureBox,用户显示接收到的图片切片。对于这些PictureBox的ID,从上到下,从左到右依次是pictureBox11、pictureBox12、...、pictureBox15、...、pictureBox55。整个服务寄宿和图片接收实现在如下的代码中。值得注意的一点是,ImageAssembler_ImageCliceReceived方法将接收到的字节数组转化成位图,依次显示到上述的25个PictureBox上。在方法上面应用了一个MethodImplAttribute特性并指定MethodImplOptions.Synchronized作为参数,所以该方法是同步执行的。也就是说,该方法处理的消息次序就是消息被交付的次序。
usingSystem;
usingSystem.Drawing;
usingSystem.IO;
usingSystem.Runtime.CompilerServices;
usingSystem.ServiceModel;
usingSystem.Threading;
usingSystem.Windows.Forms;
namespaceArtech.ImageTransfer.Service
{
publicpartialclassRecevier:Form
{
privatePictureBox[]_pictureBoxes;
privateSynchronizationContext_synchronizationContext=null;
privateint_index=0;
privateServiceHost_serviceHost=null;
publicRecevier()
{
InitializeComponent();
_pictureBoxes=newPictureBox[]{
this.pictureBox11,this.pictureBox12,this.pictureBox13,this.pictureBox14,this.pictureBox15,this.pictureBox21,this.pictureBox22,this.pictureBox23,this.pictureBox24,this.pictureBox25,this.pictureBox31,this.pictureBox32,this.pictureBox33,this.pictureBox34,this.pictureBox35,this.pictureBox41,this.pictureBox42,this.pictureBox43,this.pictureBox44,this.pictureBox45,this.pictureBox51,this.pictureBox52,this.pictureBox53,this.pictureBox54,this.pictureBox55};
ImageAssembler.ImageSliceReceived+=ImageAssembler_ImageCliceReceived;
ImageAssembler.ImageErasing+=ImageAssembler_ImageErasing;
}
[MethodImpl(MethodImplOptions.Synchronized)]
privatevoidImageAssembler_ImageCliceReceived(objectsender,ImageReceivedEventArgsargs)
{
Bitmapbitmap=null;
using(MemoryStreamstream=newMemoryStream(args.ImageSlice))
{
bitmap=newBitmap(stream);
_synchronizationContext.Send(state=>_pictureBoxes[_index++].Image=bitmap,null);
}
}
privatevoidImageAssembler_ImageErasing(objectsender,EventArgsargs)
{
_index=0;
foreach(varpictureBoxin_pictureBoxes)
{
pictureBox.Image=null;
}
}
privatevoidRecevier_Load(objectsender,EventArgse)
{
_synchronizationContext=SynchronizationContext.Current;
_serviceHost=newServiceHost(typeof(ImageTransferService));
_serviceHost.Open();
}
}
}
图1图片接收窗口
步骤四:创建图片发送程序
最后我们来编写我们的图片发送端程序,即如果对图片进行切片,并通过调用图片传输服务对切片进行发送。我们照例先来看看WCF在客户端的配置:<?xmlversion="1.0"encoding="utf-8"?>
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<bindingname="nonReliableSession">
<binaryMessageEncoding>
<readerQuotasmaxArrayLength="2147483647"/>
</binaryMessageEncoding>
<unreliabeNetworkSimulatedropRate="10"/>
<tcpTransportmaxBufferSize="2147483647"maxReceivedMessageSize="2147483647"/>
</binding>
<bindingname="reliableSession">
<reliableSessionordered="false"/>
<binaryMessageEncoding>
<readerQuotasmaxArrayLength="2147483647"/>
</binaryMessageEncoding>
<unreliabeNetworkSimulatedropRate="10"/>
<tcpTransportmaxBufferSize="2147483647"maxReceivedMessageSize="2147483647"/>
</binding>
<bindingname="orderedDelivery">
<reliableSessionordered="true"/>
<binaryMessageEncoding>
<readerQuotasmaxArrayLength="2147483647"/>
</binaryMessageEncoding>
<unreliabeNetworkSimulatedropRate="10"/>
<tcpTransportmaxBufferSize="2147483647"maxReceivedMessageSize="2147483647"/>
</binding>
</customBinding>
</bindings>
<client>
<endpointname="nonReliableSession"address="net.tcp://127.0.0.1:7777/imagetransferservice"binding="customBinding"bindingConfiguration="nonReliableSession"contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/>
<endpointname="reliableSession"address="net.tcp://127.0.0.1:8888/imagetransferservice"binding="customBinding"bindingConfiguration="reliableSession"contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/>
<endpointname="orderedDelivery"address="net.tcp://127.0.0.1:9999/imagetransferservice"binding="customBinding"bindingConfiguration="orderedDelivery"contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/>
</client>
<extensions>
<bindingElementExtensions>
<addname="unreliabeNetworkSimulate"type="Artech.ImageTransfer.Extensions.UnreliableNetworkSimulateExtensionElement,Artech.ImageTransfer.Extensions,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"/>
</bindingElementExtensions>
</extensions>
</system.serviceModel>
</configuration>
同服务寄宿端的配置一样,客户端也配置了三种自定义绑定。所不同的是,它们均多了一个额外的绑定元素UnreliableNetworkSimulateBindingElement,即我们之前创建的用于模拟不稳定网络环境的邦定元素。
图2是图片发送窗口,上边部分一个Picture,会显示通过点击Browse按钮选择的图片。当成功选择某一张用于发送的图片后,点击Send按钮将其发送。ReliableSession和OrderedDelivery两个CheckBox供用户决定是否采用可靠会话,以及有序交付机制进行图片的发送。默认情况下,并不采用可靠会话机制进行图片发送。图片的选择、切片和发送通过下面的代码实现。
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Drawing;
usingSystem.Drawing.Imaging;
usingSystem.IO;
usingSystem.ServiceModel;
usingSystem.Windows.Forms;
usingArtech.ImageTransfer.Service.Interface;
namespaceArtech.ImageTransfer.Client
{
publicpartialclassSender:Form
{
privatestring_imageSource=string.Empty;
privateIImageTransfer_nonReliableSessionProxy=null;
privateIImageTransfer_reliableSessionProxy=null;
privateIImageTransfer_orderedDeliveryProxy=null;
ChannelFactory<IImageTransfer>_nonReliableSessionFactory=newChannelFactory<IImageTransfer>("nonReliableSession");
ChannelFactory<IImageTransfer>_reliableSessionFactory=newChannelFactory<IImageTransfer>("reliableSession");
ChannelFactory<IImageTransfer>_orderedDeliveryFactory=newChannelFactory<IImageTransfer>("orderedDelivery");
publicSender()
{
InitializeComponent();
}
privateIImageTransferGetProxy()
{
if(null!=_nonReliableSessionProxy)
{
(_nonReliableSessionProxyasICommunicationObject).Close();
}
if(null!=_reliableSessionProxy)
{
(_reliableSessionProxyasICommunicationObject).Close();
}
if(null!=_orderedDeliveryProxy)
{
(_orderedDeliveryProxyasICommunicationObject).Close();
}
if(!this.checkBoxReliableSession.Checked)
{
_nonReliableSessionProxy=_nonReliableSessionFactory.CreateChannel();
(_nonReliableSessionProxyasICommunicationObject).Open();
return_nonReliableSessionProxy;
}
elseif(!this.checkBoxOrdered.Checked)
{
_reliableSessionProxy=_reliableSessionFactory.CreateChannel();
(_reliableSessionProxyasICommunicationObject).Open();
return_reliableSessionProxy;
}
else
{
_orderedDeliveryProxy=_orderedDeliveryFactory.CreateChannel();
(_orderedDeliveryProxyasICommunicationObject).Open();
return_orderedDeliveryProxy;
}
}
privatevoidbuttonOpen_Click(objectsender,EventArgse)
{
OpenFileDialogopenFileDialog=newOpenFileDialog();
if(openFileDialog.ShowDialog()==DialogResult.OK)
{
_imageSource=openFileDialog.FileName;
this.pictureBox1.Load(_imageSource);
}
this.buttonSend.Enabled=true;
}
privatebyte[]BitmapToBytes(Bitmapbitmap)
{
using(MemoryStreamms=newMemoryStream())
{
bitmap.Save(ms,ImageFormat.Bmp);
byte[]data=newbyte[ms.Length];
ms.Seek(0,SeekOrigin.Begin);
ms.Read(data,0,Convert.ToInt32(ms.Length));
returndata;
}
}
privatevoidbuttonSend_Click(objectsender,EventArgse)
{
this.buttonSend.Enabled=false;
IList<byte[]>imageSlices=newList<byte[]>();
Bitmapbmp=newBitmap(this._imageSource);
doublewidth=(double)bmp.Width/5;
doubleheight=(double)bmp.Height/5;
for(inty=0;y<5;y++)
{
for(intx=0;x<5;x++)
{
Rectanglerect=newRectangle(Convert.ToInt32(x*width),Convert.ToInt32(y*height),
Convert.ToInt32(width),Convert.ToInt32(height));
byte[]data=BitmapToBytes(bmp.Clone(rect,PixelFormat.DontCare));
imageSlices.Add(data);
}
}
IImageTransferproxy=GetProxy();
proxy.Erase();
for(inti=0;i<imageSlices.Count;i++)
{
proxy.Transfer(imageSlices[i]);
}
this.buttonSend.Enabled=true;
}
}
}
通过上面的代码我们可以发现,我们通过GetProxy()方法选择相应终结点对应的ChannelFactory<IImageTransfer>对象,并创建服务代理。在buttonSend_Click方法中,被选择的图片被均分成25个切片,并按照从上到下、从左至右的顺旬转化成字节数据,最终利用创建的服务代理发送出去。在发送之前,调用Erase服务操作通知接收端擦除已经接收到的切片。
图2图片发送端窗口
所有的编程工作完成后,我们来运行我们的程序。图3表示的是没有采用可靠会话时的图片传输情况。从中我们可以看到两接收方组装后的图片不完整,有四个切片缺失。此外,接收方组装后的切片完全是错位的。
图3没有采用可靠会话图片传输情况
图4表示的是选择了可靠会话选项,但是没有选择有序交付选项时图片传输的情况。我们可以看出,这一次解决了切片丢失的问题,但是错位的情况下依然存在。
图4选择可靠会话但不选择有序交付时图片传输情况
最后,我们同是选择可靠会话和有序交付两个选项,你在接收端将会得到一张完完整整地图片,既不会有切片丢失,也不会出现切片错位的情况。最终的结果如图5所示,这才是我们希望的。
图5同时选择可靠会话和有序交付时图片传输情况
实际上,WCF的可靠会话涉及到WS中一个重要的概念——可靠消息传输(RM:ReliableMessaging)。如果想对可靠会话有一个深入的认识,对可靠消息传输的了解是必须的。《
作者:
出处:
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章推荐
- 使命必达: 深入剖析WCF的可靠会话[实例篇](内含美女图片,定力差者慎入)
- 使命必达: 深入剖析WCF的可靠会话[编程篇](下)
- “深入剖析WCF的可靠会话”系列[共8篇]
- 使命必达: 深入剖析WCF的可靠会话[编程篇](上)
- 使命必达: 深入剖析WCF的可靠会话[共8篇]
- 使命必达: 深入剖析WCF的可靠会话[协议篇](上)
- 使命必达: 深入剖析WCF的可靠会话[概念篇]
- 使命必达: 深入剖析WCF的可靠会话[原理揭秘篇](上)
- 一起谈.NET技术,使命必达:深入剖析WCF的可靠会话
- 使命必达: 深入剖析WCF的可靠会话[原理揭秘篇](下)
- 使命必达: 深入剖析WCF的可靠会话[协议篇](下)
- 深入剖析WCF的可靠会话
- WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]
- 【WCF--初入江湖】09 可靠会话与可靠性消息
- WCF学习笔记之可靠会话
- WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[上篇]
- WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[上篇]
- [原创]WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务
- WCF把书读薄(4)——事务编程与可靠会话
- WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务