使命必達: 深入剖析WCF的可靠會話[實例篇](內含美女圖片,定力差者慎入)
通過前麵一係列的博文(《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="https://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
{
public static void ReceiveImageSlice(byte[] imageSlice)
{
if (null != ImageSliceReceived)
{
ImageSliceReceived(null, new ImageReceivedEventArgs(imageSlice));
}
}
public static void Erase()
{
if (null != ImageErasing)
{
ImageErasing(null, EventArgs.Empty);
}
}
public static event EventHandler<ImageReceivedEventArgs> ImageSliceReceived;
public static event EventHandler ImageErasing;
}
public class ImageReceivedEventArgs : EventArgs
{
public byte[] ImageSlice
{ get; private set; }
public ImageReceivedEventArgs(byte[] imageSlice)
{
this.ImageSlice = imageSlice;
}
}
}
接下來是服務傳輸服務的實現,該服務定義在如下的ImageTransferService類中。對於兩個服務操作,我們分別調用ImageAssembler的兩個對應的靜態方法提供實現。
using System.ServiceModel;
using Artech.ImageTransfer.Service.Interface;
namespace Artech.ImageTransfer.Service
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class ImageTransferService : IImageTransfer
{
public void Transfer(byte[] imageSlice)
{
ImageAssembler.ReceiveImageSlice(imageSlice);
}
public void Erase()
{
ImageAssembler.Erase();
}
}
}
步驟三:服務寄宿和圖片接收程序實現
圖片傳輸服務ImageTransferService最終被寄宿於一個Windows Forms應用中,該應用同時作為圖片接收程序使用。我們先來看看服務寄宿端的配置:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="nonReliableSession">
<binaryMessageEncoding>
<readerQuotas maxArrayLength="2147483647 "/>
</binaryMessageEncoding>
<tcpTransport maxBufferSize="2147483647 " maxReceivedMessageSize="2147483647" />
</binding>
<binding name="reliableSession">
<reliableSession ordered="false"/>
<binaryMessageEncoding>
<readerQuotas maxArrayLength="2147483647 "/>
</binaryMessageEncoding>
<tcpTransport maxBufferSize="2147483647 " maxReceivedMessageSize="2147483647" />
</binding>
<binding name="orderedDelivery">
<reliableSession ordered="true" />
<binaryMessageEncoding>
<readerQuotas maxArrayLength="2147483647 "/>
</binaryMessageEncoding>
<tcpTransport maxBufferSize="2147483647 " maxReceivedMessageSize="2147483647" />
</binding>
</customBinding>
</bindings>
<services>
<service name="Artech.ImageTransfer.Service.ImageTransferService">
<endpoint address="net.tcp://127.0.0.1:7777/imagetransferservice" binding="customBinding" bindingConfiguration="nonReliableSession" contract="Artech.ImageTransfer.Service.Interface.IImageTransfer" />
<endpoint address="net.tcp://127.0.0.1:8888/imagetransferservice" binding="customBinding" bindingConfiguration="reliableSession" contract="Artech.ImageTransfer.Service.Interface.IImageTransfer" />
<endpoint address="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作為參數,所以該方法是同步執行的。也就是說,該方法處理的消息次序就是消息被交付的次序。
using System;
using System.Drawing;
using System.IO;
using System.Runtime.CompilerServices;
using System.ServiceModel;
using System.Threading;
using System.Windows.Forms;
namespace Artech.ImageTransfer.Service
{
public partial class Recevier : Form
{
private PictureBox[] _pictureBoxes;
private SynchronizationContext _synchronizationContext = null;
private int _index = 0;
private ServiceHost _serviceHost = null;
public Recevier()
{
InitializeComponent();
_pictureBoxes = new PictureBox[]{
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)]
private void ImageAssembler_ImageCliceReceived(object sender, ImageReceivedEventArgs args)
{
Bitmap bitmap = null;
using (MemoryStream stream = new MemoryStream(args.ImageSlice))
{
bitmap = new Bitmap(stream);
_synchronizationContext.Send(state => _pictureBoxes[_index++].Image = bitmap, null);
}
}
private void ImageAssembler_ImageErasing(object sender, EventArgs args)
{
_index = 0;
foreach (var pictureBox in _pictureBoxes)
{
pictureBox.Image = null;
}
}
private void Recevier_Load(object sender, EventArgs e)
{
_synchronizationContext = SynchronizationContext.Current;
_serviceHost = new ServiceHost(typeof(ImageTransferService));
_serviceHost.Open();
}
}
}
圖1 圖片接收窗口
步驟四:創建圖片發送程序
最後我們來編寫我們的圖片發送端程序,即如果對圖片進行切片,並通過調用圖片傳輸服務對切片進行發送。我們照例先來看看WCF在客戶端的配置:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="nonReliableSession">
<binaryMessageEncoding>
<readerQuotas maxArrayLength="2147483647 "/>
</binaryMessageEncoding>
<unreliabeNetworkSimulate dropRate ="10"/>
<tcpTransport maxBufferSize="2147483647 " maxReceivedMessageSize="2147483647" />
</binding>
<binding name="reliableSession">
<reliableSession ordered="false" />
<binaryMessageEncoding>
<readerQuotas maxArrayLength="2147483647 "/>
</binaryMessageEncoding>
<unreliabeNetworkSimulate dropRate ="10"/>
<tcpTransport maxBufferSize="2147483647 " maxReceivedMessageSize="2147483647" />
</binding>
<binding name="orderedDelivery">
<reliableSession ordered="true" />
<binaryMessageEncoding>
<readerQuotas maxArrayLength="2147483647 "/>
</binaryMessageEncoding>
<unreliabeNetworkSimulate dropRate ="10"/>
<tcpTransport maxBufferSize="2147483647 " maxReceivedMessageSize="2147483647" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint name="nonReliableSession" address="net.tcp://127.0.0.1:7777/imagetransferservice" binding="customBinding" bindingConfiguration="nonReliableSession" contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/>
<endpoint name="reliableSession" address="net.tcp://127.0.0.1:8888/imagetransferservice" binding="customBinding" bindingConfiguration="reliableSession" contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/>
<endpoint name="orderedDelivery" address="net.tcp://127.0.0.1:9999/imagetransferservice" binding="customBinding" bindingConfiguration="orderedDelivery" contract="Artech.ImageTransfer.Service.Interface.IImageTransfer"/>
</client>
<extensions>
<bindingElementExtensions>
<add name="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按鈕將其發送。Reliable Session和Ordered Delivery兩個CheckBox供用戶決定是否采用可靠會話,以及有序交付機製進行圖片的發送。默認情況下,並不采用可靠會話機製進行圖片發送。圖片的選擇、切片和發送通過下麵的代碼實現。
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.ServiceModel;
using System.Windows.Forms;
using Artech.ImageTransfer.Service.Interface;
namespace Artech.ImageTransfer.Client
{
public partial class Sender : Form
{
private string _imageSource = string.Empty;
private IImageTransfer _nonReliableSessionProxy = null;
private IImageTransfer _reliableSessionProxy = null;
private IImageTransfer _orderedDeliveryProxy = null;
ChannelFactory<IImageTransfer> _nonReliableSessionFactory = new ChannelFactory<IImageTransfer>("nonReliableSession");
ChannelFactory<IImageTransfer> _reliableSessionFactory = new ChannelFactory<IImageTransfer>("reliableSession");
ChannelFactory<IImageTransfer> _orderedDeliveryFactory = new ChannelFactory<IImageTransfer>("orderedDelivery");
public Sender()
{
InitializeComponent();
}
private IImageTransfer GetProxy()
{
if(null != _nonReliableSessionProxy)
{
(_nonReliableSessionProxy as ICommunicationObject).Close();
}
if(null != _reliableSessionProxy)
{
(_reliableSessionProxy as ICommunicationObject).Close();
}
if(null != _orderedDeliveryProxy)
{
(_orderedDeliveryProxy as ICommunicationObject).Close();
}
if (!this.checkBoxReliableSession.Checked)
{
_nonReliableSessionProxy = _nonReliableSessionFactory.CreateChannel();
(_nonReliableSessionProxy as ICommunicationObject).Open();
return _nonReliableSessionProxy;
}
else if (!this.checkBoxOrdered.Checked)
{
_reliableSessionProxy = _reliableSessionFactory.CreateChannel();
(_reliableSessionProxy as ICommunicationObject).Open();
return _reliableSessionProxy;
}
else
{
_orderedDeliveryProxy = _orderedDeliveryFactory.CreateChannel();
(_orderedDeliveryProxy as ICommunicationObject).Open();
return _orderedDeliveryProxy;
}
}
private void buttonOpen_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog()== DialogResult.OK)
{
_imageSource = openFileDialog.FileName;
this.pictureBox1.Load(_imageSource);
}
this.buttonSend.Enabled = true;
}
private byte[] BitmapToBytes(Bitmap bitmap)
{
using (MemoryStream ms = new MemoryStream())
{
bitmap.Save(ms, ImageFormat.Bmp);
byte[] data = new byte[ms.Length];
ms.Seek(0, SeekOrigin.Begin);
ms.Read(data, 0, Convert.ToInt32(ms.Length));
return data;
}
}
private void buttonSend_Click(object sender, EventArgs e)
{
this.buttonSend.Enabled = false;
IList<byte[]> imageSlices = new List<byte[]>();
Bitmap bmp = new Bitmap(this._imageSource);
double width = (double)bmp.Width / 5;
double height = (double)bmp.Height / 5;
for (int y = 0; y < 5; y++)
{
for (int x = 0; x < 5; x++)
{
Rectangle rect = new Rectangle(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);
}
}
IImageTransfer proxy = GetProxy();
proxy.Erase();
for (int i = 0; i < imageSlices.Count; i++)
{
proxy.Transfer(imageSlices[i]);
}
this.buttonSend.Enabled = true;
}
}
}
通過上麵的代碼我們可以發現,我們通過GetProxy()方法選擇相應終結點對應的ChannelFactory<IImageTransfer>對象,並創建服務代理。在buttonSend_Click方法中,被選擇的圖片被均分成25個切片,並按照從上到下、從左至右的順旬轉化成字節數據,最終利用創建的服務代理發送出去。在發送之前,調用Erase服務操作通知接收端擦除已經接收到的切片。
所有的編程工作完成後,我們來運行我們的程序。圖3表示的是沒有采用可靠會話時的圖片傳輸情況。從中我們可以看到兩接收方組裝後的圖片不完整,有四個切片缺失。此外,接收方組裝後的切片完全是錯位的。
圖3 沒有采用可靠會話圖片傳輸情況
圖4表示的是選擇了可靠會話選項,但是沒有選擇有序交付選項時圖片傳輸的情況。我們可以看出,這一次解決了切片丟失的問題,但是錯位的情況下依然存在。
圖4 選擇可靠會話但不選擇有序交付時圖片傳輸情況
最後,我們同是選擇可靠會話和有序交付兩個選項,你在接收端將會得到一張完完整整地圖片,既不會有切片丟失,也不會出現切片錯位的情況。最終的結果如圖5所示,這才是我們希望的。
圖5 同時選擇可靠會話和有序交付時圖片傳輸情況
實際上,WCF的可靠會話涉及到WS中一個重要的概念——可靠消息傳輸(RM:Reliable Messaging)。如果想對可靠會話有一個深入的認識,對可靠消息傳輸的了解是必須的。《概念篇》中將會對RM的基本概念進行基本的講述。
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-27 15:04:33
上一篇:
控製並發訪問的三道屏障: WCF限流(Throttling)體係探秘[下篇]
下一篇:
使命必達: 深入剖析WCF的可靠會話[概念篇]
微軟Bing的反攻
java.util.concurrent包(5)——CountDownLatch使用
《Git版本控製管理(第2版)》——1.4 時間線
ScheduledExecutorService和timer的異同
知道ip 查服務器對應的mac
PostgreSQL 10 + PostGIS + Sharding(pg_pathman) + MySQL(fdw外部表) on ECS 部署指南(適合新用戶)
ASP.NET MVC三個重要的描述對象:ControllerDescriptor和ActionDescriptor的創建
如何用好PostgreSQL的備份與恢複?
美媒:現在是投資AI股票的最佳時機
智慧醫療促進現代醫學從治療走向預防