WCF初探-5:WCF消息交换模式之双工通讯(Duplex)
双工通讯Duplex具有以下特点:
1它可以在处理完请求之后,通过请求客户端中的回调进行响应操作
2.消息交换过程中,服务端和客户端角色会发生调换
3.服务端处理完请求后,返回给客户端的不是reply,而是callback请求。
4.Duplex模式对Bindding有特殊的要求,它要求支持Duplex MEP(Message Exchange Pattern),如WSDualHttpBinding和NetTcpBinding
注意:在WCF预定义绑定类型中,WSDualHttpBinding和NetTcpBinding均提供了对双工通信的支持,但是两者在对双工通信的实现机制上却有本质的区别。WSDualHttpBinding是基于HTTP传输协议的;而HTTP协议本身是基于请求-回复的传输协议,基于HTTP的通道本质上都是单向的。WSDualHttpBinding实际上创建了两个通道,一个用于客户端向服务端的通信,而另一个则用于服务端到客户端的通信,从而间接地提供了双工通信的实现。而NetTcpBinding完全基于支持双工通信的TCP协议。
我今天的实例讲的就是双工通讯的一个使用场景订阅-发布模式,此时消息的双方变成了订阅者和发布者。订阅者有两个操作(订阅消息、取消订阅),当订阅者订阅消息后,发布者就开始向订阅者广播消息,当订阅者取消订阅后,就不会接收到广播的消息。具体如下图所示:
接下来我们我们创建基于WCF的双工通讯的订阅与发布模式的服务。工程结构如下图所示:
Publisher(发布者)和Subscriber(订阅者)都是Winform工程,我们把发布者作为服务端,订阅者作为客户端,发布者还需要承载寄宿服务。如下图设置好发布者和订阅者的界面,
发布者有一个寄宿服务的lable显示服务是否寄宿成功,一个消息文本框和一个发布按钮,输入文本后,点击发布就可以向订阅的客户端广播消息。
订阅者的界面上有一个消息接收的listbox,以及订阅消息和取消订阅按钮,还有一个输入客户端名称的文本框,界面如下图所示:
接下来我们开始实际的代码操作,首先完成发布者(服务端)的代码实现,创建IPublisher.cs文件,定义服务接口和回调接口,代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.ServiceModel;
- namespace Publisher
- {
- [ServiceContract(CallbackContract = typeof(IPublisherEvents))]
- public interface IPublisher
- {
- [OperationContract(IsOneWay = true)]
- void Subscriber(string clientID,string clientName); //订阅消息
- [OperationContract(IsOneWay = true)]
- void UnSubscriber(string clientID, string clientName); //取消订阅
- }
- public interface IPublisherEvents
- {
- [OperationContract(IsOneWay = true)]
- void PublishMessage(string message); //发布消息
- }
- }
接口里面只定义了订阅者(客户端)调用的订阅消息和取消订阅的方法,以及服务端调用客户端的回调方法PublishMessage,然后我们在FormPublisher.cs里面实现该接口,具体代码如下:
- using System;
- using System.Collections.Generic;
- using System.Windows.Forms;
- using System.ServiceModel;
- using System.Threading;
- namespace Publisher
- {
- [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
- public partial class FormPublisher : Form, IPublisher, IDisposable
- {
- //定义回调客户端集合
- public static List<IPublisherEvents> ClientCallbackList { get; set; }
- public FormPublisher()
- {
- InitializeComponent();
- ClientCallbackList = new List<IPublisherEvents>();
- }
- //寄宿服务
- private ServiceHost _host = null;
- private void FormPublisher_Load(object sender, EventArgs e)
- {
- _host = new ServiceHost(typeof(Publisher.FormPublisher));
- _host.Open();
- this.label1.Text = "MessageService Opened.";
- }
- //关闭窗体
- private void FormPublisher_FormClosing(object sender, FormClosingEventArgs e)
- {
- if (_host != null)
- {
- _host.Close();
- IDisposable host = _host as IDisposable;
- host.Dispose();
- }
- }
- //发布消息
- private void btn_Publish_Click(object sender, EventArgs e)
- {
- var list =Publisher.FormPublisher.ClientCallbackList;
- if (list == null || list.Count == )
- return;
- lock (list)
- {
- foreach (var client in list)
- {
- client.PublishMessage(this.txt_Message.Text);
- }
- }
- }
- //实现订阅
- public void Subscriber(string clientID, string clientName)
- {
- var client = OperationContext.Current.GetCallbackChannel<IPublisherEvents>();
- var sessionid = OperationContext.Current.SessionId;
- MessageBox.Show( string.Format("客户端{0} 开始订阅消息。", clientName));
- OperationContext.Current.Channel.Closing += new EventHandler(Channel_Closing);
- ClientCallbackList.Add(client);
- }
- //取消订阅
- public void UnSubscriber(string clientID, string clientName)
- {
- var client = OperationContext.Current.GetCallbackChannel<IPublisherEvents>();
- var sessionid = OperationContext.Current.SessionId;
- MessageBox.Show(string.Format("客户端{0}取消订阅消息", clientName));
- OperationContext.Current.Channel.Closing += new EventHandler(Channel_Closing);
- ClientCallbackList.Remove(client);
- }
- //关闭通道,移除回调客户端
- void Channel_Closing(object sender, EventArgs e)
- {
- lock (ClientCallbackList)
- {
- ClientCallbackList.Remove((IPublisherEvents)sender);
- }
- }
- }
- }
注意:当前使用了实例上下文模式为单例模式,我们启用的是同一个实例上下文模式,即客户端共享同一个同一个会话,关于实例模式有三种:
1. Single —— 表示所有的客户端共享一个会话(服务对象)(服务关闭时才会销毁服务对象)
2. PerCall —— 表示每次调用都会创建一个会话(服务对象)(调用完毕后就会销毁服务对象)
3. PerSession —— 表示为每个连接(每个客户端代理对象) 创建一个会话(服务对象),只有指定IsTerminating=true的操作被调用,或者是设定的SessionTimeout超时的时候,服务对象会被销毁。但支持Session的Binding只有:WSHttpBinding、WSDualHttpBinding、WSFederationHttpBinding、NetTcpBinding。
关于实例上下文模式,我将在后期博文中详细介绍。
完成后,我们就开始配置我们的服务端的”ABC”,服务端的配置文件如下:
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <system.web>
- <compilation debug="true"/>
- </system.web>
- <system.serviceModel>
- <services>
- <service name="Publisher.FormPublisher">
- <endpoint address="" binding="netTcpBinding" bindingConfiguration="netTcpExpenseService_ForSupplier" contract="Publisher.IPublisher">
- <identity>
- <dns value="localhost"/>
- </identity>
- </endpoint>
- <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
- <host>
- <baseAddresses>
- <add baseAddress="net.tcp://172.0.0.1:9999/WcfDuplexService/"/>
- <add baseAddress="http://172.0.0.1:9998/WcfDuplexService"/>
- </baseAddresses>
- </host>
- </service>
- </services>
- <behaviors>
- <serviceBehaviors>
- <behavior>
- <serviceMetadata httpGetEnabled="True"/>
- <serviceDebug includeExceptionDetailInFaults="False"/>
- </behavior>
- </serviceBehaviors>
- </behaviors>
- <bindings>
- <netTcpBinding>
- <binding name="netTcpExpenseService_ForSupplier" closeTimeout="00:01:00"
- openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
- transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
- hostNameComparisonMode="StrongWildcard" listenBacklog="10"
- maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxConnections="10"
- maxReceivedMessageSize="2147483647">
- <readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647"
- maxBytesPerRead="4096" maxNameTableCharCount="16384" />
- <reliableSession ordered="true" inactivityTimeout="00:10:00"
- enabled="false" />
- <security mode="None">
- <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
- <message clientCredentialType="Windows" />
- </security>
- </binding>
- </netTcpBinding>
- </bindings>
- </system.serviceModel>
- </configuration>
到此我们的发布者(服务端)的代码完成,编译后启动我们的Publisher.exe就可以看到服务寄宿成功的界面如下图所示:
接下来我们在Subscriber项目中添加服务引用,如下图所示:
注意:我们选择http://172.0.0.1:9998/WcfDuplexService地址,因为我们的服务已经采用了元数据地址发布,但是你们在引用的时候把配置文件和引用地址中的172.0.0.1改为
localhost或者是127.0.0.1,再或者是你们本机的IP。要不然服务地址是不准确的,无法引用。(评论中有人出现了这个问题呢)
接下来我们实现FormSubscriber.cs窗体的代码:
- using System;
- using System.Windows.Forms;
- using System.ServiceModel;
- using System.Threading;
- using Subscriber.WcfDuplexService;
- namespace Subscriber
- {
- public partial class FormSubscriber : Form, IPublisherCallback
- {
- PublisherClient proxy = null;
- public FormSubscriber()
- {
- InitializeComponent();
- InstanceContext instance = new InstanceContext(this);
- proxy = new PublisherClient(instance);
- btn_cancle.Enabled = false;
- }
- //实现客户端回调函数
- public void PublishMessage(string message)
- {
- string msg = string.Format("来自服务端的广播消息 : {0}",message);
- lst_getMsg.Items.Add(msg);
- }
- //订阅消息
- private void btn_ok_Click(object sender, EventArgs e)
- {
- btn_ok.Enabled = false;
- btn_cancle.Enabled = true;
- string ClientID = System.Guid.NewGuid().ToString();
- string ClientName = this.textBox1.Text;
- proxy.Subscriber(ClientID, ClientName);
- }
- //取消订阅
- private void btn_cancle_Click(object sender, EventArgs e)
- {
- btn_ok.Enabled = true;
- btn_cancle.Enabled = false;
- string ClientID = System.Guid.NewGuid().ToString();
- string ClientName = this.textBox1.Text;
- proxy.UnSubscriber(ClientID, ClientName);
- }
- }
- }
到此,我们整个解决方案已经完成,接下来,我们运行程序来验证我们需要的结果,首先启动发布者(即服务端),再启动订阅者(即客户端,注意:这里我们启动两个,方便验证程序效果),运行效果如下:
效果1:client1和client2都订阅消息,此时两个客户端都能收到广播的消息
效果2:client1订阅消息和client2取消订阅,此时只有client1能收到广播的消息
效果3:client1取消订阅和client2订阅消息,此时只有client2收到广播的消息
WCF初探-5:WCF消息交换模式之双工通讯(Duplex)的更多相关文章
- WCF消息交换模式之双工通讯(Duplex)
WCF消息交换模式之双工通讯(Duplex) 双工通讯Duplex具有以下特点: 1它可以在处理完请求之后,通过请求客户端中的回调进行响应操作 2.消息交换过程中,服务端和客户端角色会发生调换 3.服 ...
- [老老实实学WCF] 第十篇 消息通信模式(下) 双工
老老实实学WCF 第十篇 消息通信模式(下) 双工 在前一篇的学习中,我们了解了单向和请求/应答这两种消息通信模式.我们知道可以通过配置操作协定的IsOneWay属性来改变模式.在这一篇中我们来研究双 ...
- wcf_消息通信模式(下) 双工通讯
原文:[老老实实学WCF] 第十篇 消息通信模式(下) 双工 第十篇 消息通信模式(下) 双工 在前一篇的学习中,我们了解了单向和请求/应答这两种消息通信模式.我们知道可以通过配置操作协定的IsOne ...
- WCF学习笔记之消息交换模式
在WCF通信中,有三种消息交换模式,OneWay(单向模式), Request/Reponse(请求回复模式), Duplex(双工通信模式)这三种通信方式.下面对这三种消息交换模式进行讲解. 1. ...
- WCF系列教程之消息交换模式之请求与答复模式(Request/Reply)
1.使用WCF请求与答复模式须知 (1).客户端调用WCF服务端需要等待服务端的返回,即使返回类型是void (2).相比Duplex来讲,这种模式强调的是客户端的被动接受,也就是说客户端接受到响应后 ...
- WCF初探-3:WCF消息交换模式之单向模式
单向模式(One-Way Calls): 在这种交换模式中,存在着如下的特征: 只有客户端发起请求,服务端并不会对请求进行回复 不能包含ref或者out类型的参数 没有返回值,返回类型只能为void ...
- WCF初探-4:WCF消息交换模式之请求与答复模式
请求与答复模式( Request/Reply) 这种交换模式是使用最多的一中,它有如下特征: 调用服务方法后需要等待服务的消息返回,即便该方法返回 void 类型 相比Duplex来讲,这种模式强调的 ...
- WCF消息交换模式之请求-响应模式
WCF的消息交换模式(MEP)有三种:请求/响应.单向模式和双工模式.WCF的默认MEP是请求/响应模式. 请求/响应模式操作签名代码如下,无需指定模式,默认就是. [OperationContrac ...
- WCF系列教程之WCF消息交换模式之单项模式
1.使用WCF单项模式须知 (1).WCF服务端接受客户端的请求,但是不会对客户端进行回复 (2).使用单项模式的服务端接口,不能包含ref或者out类型的参数,至于为什么,请参考C# ref与out ...
随机推荐
- 记录重置css样式
;} ol,ul{;;} table {; } caption, th, td { font-weight: normal; text-align: left; } a img, iframe { b ...
- curl常用选项详解
curl常用选项详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 又是下班的时间了,让我们一起来学习一下今天的Linux命令吧~我一半只把自己常用的参数列出来,其他的有但是我们几 ...
- 5.echo(),print(),print_r()的区别
echo是PHP语句, print和print_r是函数,语句没有返回值,函数可以有返回值(即便没有用) print() 只能打印出简单类型变量的值(如int,string) print_r() ...
- JS中同名函数有效执行顺序
html中如果出现函数同名时:如果有多个外部引入的js文件,例如a.js和b.js(引入顺序假定是a.js,然后是b.js),同时html中本身也有内部的js.那么针对 出现函数名一样的情况时,无论他 ...
- 《zw版·Halcon-delphi系列原创教程》 Halcon分类函数012,polygon,多边形
<zw版·Halcon-delphi系列原创教程> Halcon分类函数012,polygon,多边形 为方便阅读,在不影响说明的前提下,笔者对函数进行了简化: :: 用符号“**”,替换 ...
- mysql之各种命令总结
1:使用SHOW语句找出在服务器上当前存在什么数据库:mysql> SHOW DATABASES;2:2.创建一个数据库MYSQLDATAmysql> CREATE DATABASE MY ...
- 数据可视化:Echart中k图实现动态阈值报警及实时更新数据
1 目标 使用Echart的k图展现上下阈值,并且当真实值超过上阈值或低于下阈值时候,标红报警. 2 实现效果 如下:
- php缓冲区 sapi缓冲区
<?php#设置php.ini中output_buffering = 32#使用apache可以看到效果 #nginx+php-fpm看不到效果 nginx缓存 sockets通信问题?#imp ...
- 谈谈对AOP的理解
Aspect Oriented Programming 面向切面编程.解耦是程序员编码开发过程中一直追求的.AOP也是为了解耦所诞生. 具体思想是:定义一个切面,在切面的纵向定义处理方法,处理完成之 ...
- EventBus使用详解(二)——EventBus使用进阶
一.概述 前一篇给大家装简单演示了EventBus的onEventMainThread()函数的接收,其实EventBus还有另外有个不同的函数,他们分别是: 1.onEvent2.onEventMa ...