现在的位置: 首页 > 综合 > 正文

使命必达: 深入剖析WCF的可靠会话[实例篇](内含美女图片,定力差者慎入)

2014年04月16日 ⁄ 综合 ⁄ 共 6508字 ⁄ 字号 评论关闭

通过前面一系列的博文(《WCF 并发(Concurrency)的本质》、《并发中的同步》、《实践重于理论》、《并发与实例上下文模式》、《回调与并发》、《ConcurrencyMode.Multiple 模式下的WCF服务就一定是并发执行的吗[上篇]》、《ConcurrencyMode.Multiple 模式下的WCF服务就一定是并发执行的吗[下篇]》、《控制并发访问的三道屏障[上篇]》和《控制并发访问的三道屏障[下篇]》),我对WCF的并发体系进行了深入的剖析,在接下来的博文中,我只要专注于WCF的可靠会话机制。

作为一个通信基础平台,WCF必须保证通信的可靠性。由于消息交换是WCF采用的通信手段,通信可靠性的保障体现在确保消息的可靠传输。WCF本质上是一个消息处理框架,作为整个消息交换系统的两个终端,即发送端和接收端。换句话说,WCF仅仅负责对消息的发送和接收,一旦消息通过WCF的信道层进入了网络,就脱离了WCF的控制范围。但是,由于网络环境的限制,网络层不能百分之百地确保对消息的有效交付。如何克服中间环节的制约,确保从一端发送的消息能够被有效地交付给另一端,这就是可靠消息传输(Reliable Messaging)需要解决的问题。WCF通过可靠会话(Reliable Sessions)实现了种种端到端(End to End)的可靠消息传输。

为了让读者对可靠会话的作用现有一个直观的认识,我们先来做一个很有意思的实例演示。接下来我们将要演示的实例是对可靠会话确保WCF消息传输的可靠性的一个直观的反应,也是早年微软推广WCF技术频繁使用的案例:图片传输。在客户端,我们选择一张图片,并对它进行切片,最后通过调用WCF服务将每一个切片依次传输到服务端。服务端则按照切片被接收到的顺序重新组装成一张完整的图片。如果中间有任何一张切片丢失,服务端最终组装图片将不会完整;如果服务端切片接收的次序和发送顺序不一致,将会造成组装后的图片并不能还原其发送前的模样。在这里,我们充分利用了WCF中的可靠会话提供了可靠而有序的消息交付。

不稳定的网络是造成消息丢失最主要的因素,但是在本机环境下模拟不稳定的网络是一件比较困难的事情。但是,虽然我们不能让消息在网络传输层中丢失,但是我们可以让它在WCF的信道层中丢失。如何实现这样的目的呢,相应阅读过《WCF技术剖析(卷1)》第3章的读者会很快想到可以采用自定义信道的方式。

步骤一:通过自定义信道模拟不稳定的网络

为了对网络传输过程中的丢包率能够进行动态控制,我特意创建一个特殊的类型MessageInspector。MessageInspector定义如下,只读属性DropRate表示丢包率,ProcessMessage对传入的消息进行处理,如果返回为Null,意味着消息的丢失。MessageInspector定义如下。

using System;

using System.ServiceModel.Channels;

namespace Artech.ImageTransfer.Extensions

{

    public class MessageInspector

    {

        public int DropRate{ get; private set; }

        public Random Randomizer{ get; private set; }

        public MessageInspector(int dropRate)

        {

            this.DropRate = dropRate;

            this.Randomizer = new Random();

        }

        public virtual void ProcessMessage(ref Message message)

        {

            int randomNumber = this.Randomizer.Next(100);

            if (randomNumber <= this.DropRate)

            {

                message = null;

            }

        }

    }

}

接下来我们就来创建这个用于模拟不稳定网络环境的自定义信道UnreliableNetworkSimulateChannel。由于我们即将演示的实例采用TCP传输方式,所以我们让UnreliableNetworkSimulateChannel实现了IDuplexSessionChannel接口。UnreliableNetworkSimulateChannel通过MessageInspector对象对传入的消息进行加工(根据丢包率随即地丢弃)。MessageInspector在构造函数中创建,而丢包率通过参数传入。除了Send方法,几乎所有的成员都是调用InnerChannel相应的方法或者返回同名的属性。由于在《WCF技术剖析(卷1)》我们有过对如何自定义信道的专门介绍,在这里我们就不在多做重复的讲述了。

using System;

using System.ServiceModel.Channels;

namespace Artech.ImageTransfer.Extensions

{

    public class UnreliableNetworkSimulateChannel : IDuplexSessionChannel

    {

        public IDuplexSessionChannel InnerChannel{ get; private set; }

        public MessageInspector MessageInspector{ get; private set; }

        public UnreliableNetworkSimulateChannel(IDuplexSessionChannel innerChannel, int dropRate)

        {

            this.InnerChannel = innerChannel;

            this.MessageInspector = new MessageInspector(dropRate);

        }

        public IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback, object state)

        {

            return this.InnerChannel.BeginReceive(timeout, callback, state);

        }       

        public void Send(Message message, TimeSpan timeout)

        {

            this.MessageInspector.ProcessMessage(ref message);

            if(null != message)

            {

                this.InnerChannel.Send(message, timeout);

            }

        }

        public void Send(Message message)

        {

            this.MessageInspector.ProcessMessage(ref message);

            if (null != message)

            {

                this.InnerChannel.Send(message);

            }

        }

        //其他成员:直接调用InnerChannel的相应的方法或者返回同名属性

    }

}

通过上面的代码我们可以看到,在Send方法中,消息对象会先传入MessageInspector的ProcessMessage方法中,如果返回值不为空,将其递交给InnerChannel,反之意味着消息在信道层中丢失。接下来我们为该自定义信道创建信道管理器,由于该信道只在客户端使用,我们只需要为之创建信道工厂即可(Channel Factory)。UnreliableNetworkSimulateChannel对应的信道工厂UnreliableNetworkSimulateChannelFactory<TChannel>定义如下。

using System;

using System.ServiceModel.Channels;

using System.ServiceModel;

namespace Artech.ImageTransfer.Extensions

{

    public class UnreliableNetworkSimulateChannelFactory<TChannel> : ChannelFactoryBase<IDuplexSessionChannel>

    {

        public int DropRate {get; private set;}

        public IChannelFactory<TChannel> InnerChannelFactory{ get; private set; }

        public UnreliableNetworkSimulateChannelFactory(BindingContext context, int dropRate):base(context.Binding)

        {

            this.InnerChannelFactory = context.BuildInnerChannelFactory<TChannel>();

            this.DropRate = dropRate;

        }

        protected override IDuplexSessionChannel OnCreateChannel(EndpointAddress address, Uri via)

        {

            var innerChannel = (IDuplexSessionChannel)this.InnerChannelFactory.CreateChannel(address, via);

            return new UnreliableNetworkSimulateChannel(innerChannel,this.DropRate);

        }

        protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)

        {

            return this.InnerChannelFactory.BeginOpen(timeout, callback, state);

        }

        protected override void OnEndOpen(IAsyncResult result)

        {

            this.InnerChannelFactory.EndOpen(result);

        }

        protected override void OnOpen(TimeSpan timeout)

        {

            this.InnerChannelFactory.Open(timeout);

        }

    }

}

由于WCF信道栈的缔造者为绑定,而信道管理器(信道工厂或者信道监听器)最终借助于绑定元素而发送作用。为此,我们为我们创建的信道工厂创建了如下一个绑定元素:UnreliableNetworkSimulateBindingElement。

using System.ServiceModel.Channels;

namespace Artech.ImageTransfer.Extensions

{

    public class UnreliableNetworkSimulateBindingElement : BindingElement

    {

        public int DropRate { get; set; }

        public UnreliableNetworkSimulateBindingElement(int dropRate)

        {

            this.DropRate = dropRate;

        }

        public override BindingElement Clone()

        {

            return new UnreliableNetworkSimulateBindingElement(this.DropRate);

        }

        public override T GetProperty<T>(BindingContext context)

        {

            return context.GetInnerProperty<T>();

        }

        public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)

        {

            return (IChannelFactory<TChannel>)new UnreliableNetworkSimulateChannelFactory<TChannel>(context, this.DropRate);

        }

    }

}

为了使上面的绑定元素具有可配制性,我们还需要为之创建相应的配置元素。在WCF编程模型下,我们只需要集成BindingElementExtensionElement类即可。在下面定义的UnreliableNetworkSimulateExtensionElement,我们将丢包率定义成配置属性,该属性默认值为20(20%丢包率)。

using System;

using System.Configuration;

using System.ServiceModel.Channels;

using System.ServiceModel.Configuration;

namespace Artech.ImageTransfer.Extensions

{

    public class UnreliableNetworkSimulateExtensionElement:BindingElementExtensionElement

    {

        [ConfigurationProperty("dropRate", IsRequired = false, DefaultValue = 20)]

        public int DropRate

        {

            get

            {

                return (int)this["dropRate"];

            }

            set

            {

                this["dropRate"] = value;

            }

        }

        public override Type BindingElementType

        {

            get { return typeof(UnreliableNetworkSimulateBindingElement); }

        }

        protected override BindingElement CreateBindingElement()

        {

            return new UnreliableNetworkSimulateBindingElement(this.DropRate);

        }

    }

}

步骤二:创建图片传输服务

解决了对不稳定网络环境的模拟问题,我们现在正式来创建我们用于图片传输的WCF服务。先来看看服务契约的定义。服务契约IImageTransfer具有两个单向(One-Way)服务操作。Transfer方法用于对图片切片(以字节数组的形式)的传输,而Erase则用于通知接收端将之前接收的图片删除。

using System.ServiceModel;

namespace Artech.ImageTransfer.Service.Interface

{

    [ServiceContract(Namespace="http://www.artech.com/")]

    public interface IImageTransfer

    {

        [OperationContract(IsOneWay = true)]

        void Transfer(byte[] imageSlice);

        [OperationContract(IsOneWay = true)]

        void Erase();

    }

}

服务端需要将接收到的图片切片组装成一个完整的图片,我将图片组装的功能通过如下一个叫做ImageAssembler的静态类来提供。对应于服务契约定义的两个服务操作,ImageAssembler中定义两个静态事件ImageSliceReceived和ImageErasing。这两个事件分别通过静态方法ReceiveImageSlice和Erase出发。事件ImageSliceReceived的事件参数类型为ImageReceivedEventArgs ,它和ImageAssembler定义如下。

using System;

namespace Artech.ImageTransfer.Service

{

    public static class ImageAssembler

    {

抱歉!评论已关闭.