Wcf 双工通信的应用
概述
双工(Duplex)模式的消息交换方式体现在消息交换过程中,参与的双方均可以向对方发送消息。基于双工MEP消息交换可以看成是多个基本模式下(比如请求-回复模式和单项模式)消息交换的组合。双工MEP又具有一些变体,比如典型的订阅-发布模式就可以看成是双工模式的一种表现形式。双工消息交换模式使服务端回调(Callback)客户端操作成为可能。
在Wcf中不是所有的绑定协议都支持回调操作,BasicHttpBinding,WSHttpBinding绑定协议不支持回调操作;NetTcpBinding和NetNamedPipeBinding绑定支持回调操作;WSDualHttpBinding绑定是通过设置两个HTTP信道来支持双向通信,所以它也支持回调操作。
两种典型的双工MEP
1.请求过程中的回调
这是一种比较典型的双工消息交换模式的表现形式,客户端在进行服务调用的时候,附加上一个回调对象;服务在对处理该处理中,通过客户端附加的回调对象(实际上是调用回调服务的代理对象)回调客户端的操作(该操作在客户端执行)。整个消息交换的过程实际上由两个基本的消息交换构成,其一是客户端正常的服务请求,其二则是服务端对客户端的回调。两者可以采用请求-回复模式,也可以采用单向(One-way)的MEP进行消息交换。下图描述了这样的过程,服务调用和回调都采用请求-回复MEP。

2.订阅-发布
订阅-发布模式是双工模式的一个典型的变体。在这个模式下,消息交换的双方变成了订阅者和发布者,若干订阅者就某个主题向发布者申请订阅,发布者将所有的订阅者保存在一个订阅者列表中,在某个时刻将主题发送给该主题的所有订阅者。实际上基于订阅-发布模式的消息交换也可以看成是两个基本模式下消息交换的组合,申请订阅是一个单向模式的消息交换(如果订阅者行为得到订阅的回馈,该消息交换也可以采用请求-回复模式);而主题发布也是一个基于单向模式的消息交换过程。订阅-发布消息交换模式如下图所示。

示例
接下来我们将会创建一个简单的Wcf通信服务,包括使使用NetTcpBinding实现双工通信,和监控双工通信过程中的客户端和服务端一方断开后的捕捉事件。
项目如图所示

第一步:
先创建IGateWayService和INotifyCallBack接口
[ServiceContract(CallbackContract = typeof(INotifyCallBack))]
public interface IGateWayService
{
[OperationContract]
void RegisterClient(string clientName);
[OperationContract]
string GetData(int value); [OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
} // 使用下面示例中说明的数据约定将复合类型添加到服务操作。
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello "; [DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
} [DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
INotifyCallBack.cs如下:
public interface INotifyCallBack
{
[OperationContract(IsOneWay = true)]
void NotifyFunction(string sender);
}
记住在IGateWayService接口上方设置Attribute [ServiceContract(CallbackContract = typeof(INotifyCallBack))] 这样设置表示这个接口是支持回调的。
接下来定义一个ClientRegisterInfo.cs来定义客户端的名字和客户端的INotifyCallBack属性,再定义一个Timer 来调用INotifyCallBack给客户端发送消息。再通过
wcf 的ICommunicationObject来定义通信出错和关闭的事件。
public class ClientRegisterInfo
{
public ClientRegisterInfo()
{
_senderTimer.Elapsed += OnSenderMessage;
_senderTimer.Start();
} private void OnSenderMessage(object sender, ElapsedEventArgs e)
{
if (_notifyCallBack != null)
{
var communication = _notifyCallBack as ICommunicationObject;
if(communication.State==CommunicationState.Opened)
_notifyCallBack.NotifyFunction(DateTime.Now.ToString());
}
} public Timer _senderTimer=new Timer(10*1000); private INotifyCallBack _notifyCallBack; public INotifyCallBack NotifyCallBack
{
get { return _notifyCallBack; }
set
{
lock (_syncNotifyObj)
{
_notifyCallBack = value;
if (_notifyCallBack != null)
{
var communication = _notifyCallBack as ICommunicationObject;
if (communication != null)
{
communication.Closed += OnChannelClose;
communication.Faulted += OnChannelFault;
}
}
}
}
} private readonly object _syncNotifyObj = new object(); private void OnChannelFault(object sender, EventArgs e)
{ ClientInfoCache.Instance.Remove(this);
} private void OnChannelClose(object sender, EventArgs e)
{ ClientInfoCache.Instance.Remove(this);
} public string ClientName { get; set; }
}
再定义一个单例来保存客户端的信息。
public class ClientInfoCache
{
private static readonly object SyncObj = new object(); private static ClientInfoCache _instance; public static ClientInfoCache Instance
{
get
{
lock (SyncObj)
{
if (_instance == null)
_instance = new ClientInfoCache();
}
return _instance;
}
} private ClientInfoCache()
{
_clientList = new List<ClientRegisterInfo>();
} private List<ClientRegisterInfo> _clientList; private static object SyncOperator = new object(); /// <summary>
/// Add client entity
/// </summary>
/// <param name="entity">client entity</param>
public void Add(ClientRegisterInfo entity)
{
if (entity == null) return;
lock (SyncOperator)
{
var findClient =
_clientList.FirstOrDefault(
t => t.ClientName.Equals(entity.ClientName, StringComparison.OrdinalIgnoreCase));
if (findClient == null)
_clientList.Add(entity);
else
{
findClient.NotifyCallBack = entity.NotifyCallBack;
}
}
} /// <summary>
/// Remove client
/// </summary>
/// <param name="entity">Client entity</param>
public void Remove(ClientRegisterInfo entity)
{
lock (SyncOperator)
{
_clientList.Remove(entity);
}
}
}
再新建个控制台运应程序来启动Wcf,代码如下:
public class Program
{
static void Main(string[] args)
{
StartListener();
} private static void StartListener()
{
try
{
using (var host = new ServiceHost(typeof(GateWayService)))
{
host.Opened += delegate
{
Console.WriteLine("[Server] Begins to listen request on " + host.BaseAddresses[0]);
}; host.Open();
Console.Read();
}
}
catch (Exception ex)
{ }
}
}
在App.config设置配置如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="longTimeoutBinding" closeTimeout="01:10:00" openTimeout="01:10:00"
receiveTimeout="10:10:00" sendTimeout="10:10:00" maxBufferPoolSize="655350000"
maxBufferSize="655350000" maxReceivedMessageSize="655350000">
<readerQuotas maxDepth="32" maxStringContentLength="655350000"
maxArrayLength="655350000" maxBytesPerRead="655350000" maxNameTableCharCount="655350000" />
<reliableSession inactivityTimeout="23:59:59" />
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="NewBehavior">
<serviceMetadata httpGetEnabled="True" httpGetUrl="Http://localhost:7789/" httpsGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False" />
<serviceThrottling maxConcurrentCalls="1000" maxConcurrentSessions="1000" maxConcurrentInstances="1000" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="WcfService.GateWayService" behaviorConfiguration="NewBehavior" >
<endpoint address="net.tcp://localhost:7788/GatewayService.svc" binding="netTcpBinding" contract="WcfService.IGateWayService" name="WcfService_GateWayService" bindingConfiguration="longTimeoutBinding" >
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" ></endpoint>
<host >
<baseAddresses >
<add baseAddress="net.tcp://localhost:7788/GatewayService.svc" />
<add baseAddress="Http://localhost:7789/" />
</baseAddresses>
</host >
</service>
</services>
</system.serviceModel>
</configuration>
longTimeoutBinding是设置传输的属性,如最大传输大小,TimeOut的时间等。
在客户端新建个WcfCallBack.cs 继承IGateWayServiceCallback接口,代码如下。
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class WcfCallBack : IGateWayServiceCallback
{
public void NotifyFunction(string sender)
{
Console.WriteLine("Get a message,message info is {0}", sender);
}
}
设置属性[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]表示服务器是通过并发的给客户端来发送消息的。
控制台代码如下
class Program
{
private static GateWayServiceClient _client;
static void Main(string[] args)
{
var cb = new WcfCallBack();
var context = new InstanceContext(cb);
_client = new GateWayServiceClient(context);
_client.RegisterClient("Test1");
((ICommunicationObject)_client).Closed += OnChannelClose;
((ICommunicationObject)_client).Faulted += OnChannelFaulted;
Console.WriteLine("Input Q to exit.");
while (string.Compare(Console.ReadLine(), ConsoleKey.Q.ToString(), StringComparison.OrdinalIgnoreCase) != 0)
{ }
} private static void OnChannelFaulted(object sender, EventArgs e)
{
if (FaultedEvent != null)
FaultedEvent(sender, e);
} private static void OnChannelClose(object sender, EventArgs e)
{
if (CloseEvent != null)
CloseEvent(sender, e);
} public static EventHandler CloseEvent; public static EventHandler FaultedEvent;
}
运行的结果如下图:

当我关闭客户端时,能捕捉到Closed和Faulted事件

当我关闭服务端时,在客户端能捕捉到Faulted事件

总结:
Wcf 通信使用简单,功能丰富。
Wcf 双工通信的应用的更多相关文章
- WCF 双工通信
注释:本学习是参考Artech大神的资料: 在WCF 实现双工通信 在这里我就不介绍双工通信的概念了,我写博客的目的是检测自己掌握情况,看我wcf通信后,觉得纸上得来终觉浅,绝知此事要躬行. 我使用的 ...
- WCF双工通信笔记
1,Dupex(双工) MEP(消息交换模式),服务端回调(Callback)客户端操作 2,客户端调用服务时,附加上一个回调对象(InstanceContext).服务端处理服务请求时,通过该回调对 ...
- WCF双工通信单工通信
1.单工模式 单向通信,指通信只有一个方向进行,即从客户端流向服务,服务不会发送响应,而客户端也不会期望会有响应.这种情况下,客户端发送消息,然后继续执行 运行后报错: 2.双工模式 双工模式的特点是 ...
- wcf双工通信
一直以为感觉双工没弄懂,着实觉得很惆怅,在网上了解下双工的一些特点,直接上代码,以便以后项目中用的着: service层: 定义一个IDuplexHello服务接口 [ServiceContract( ...
- [转载]WCF实现双工通信
双工(Duplex)模式的消息交换方式体现在消息交换过程中,参与的双方均可以向对方发送消息.基于双工MEP消息交换可以看成是多个基本模式下(比如请求-回复模式和单项模式)消息交换的组合.双工MEP又具 ...
- 我的WCF之旅(3):在WCF中实现双工通信
双工(Duplex)模式的消息交换方式体现在消息交换过程中,参与的双方均可以向对方发送消息.基于双工MEP消息交换可以看成是多个基本模式下(比如请求-回复模式和单项模式)消息交换的组合.双工MEP又具 ...
- 在WCF中实现双工通信
双工(Duplex)模式的消息交换方式体现在消息交换过程中,参与的双方均可以向对方发送消息.基于双工MEP消息交换可以看成是多个基本模式下(比如请求-回复模式和单项模式)消息交换的组合.双工MEP又具 ...
- 利用WCF双工模式实现即时通讯
概述 WCF陆陆续续也用过多次,但每次都是浅尝辄止,以将够解决问题为王道,这几天稍闲,特寻了些资料看,昨晚尝试使用WCF的双工模式实现了一个简单的即时通讯程序,通过服务端转发实现客户端之间的通讯.这只 ...
- [SignalR]SignalR与WCF双工模式结合实现服务端数据直推浏览器端
原文:[SignalR]SignalR与WCF双工模式结合实现服务端数据直推浏览器端 之前开发基于WinForm监控的软件,服务端基于Wcf实现,里面涉及双工模式,在客户端里面,采用心跳包机制保持与服 ...
随机推荐
- 廖雪峰js教程笔记 2
arguments JavaScript还有一个免费赠送的关键字arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数.arguments类似Array但它不是一个Arr ...
- HTTP基础07--认证
何为认证 BASIC 认证 是从 HTTP/1.0 就定义的认证方式.即便是现在仍有一部分的网站会使用这种认证方式.是 Web 服务器与通信客户端之间进行的认证方式. 步骤 1: 当请求的资源需要 B ...
- [技术学习]js继承
今天又看了一遍js的面向对象方面的知识,重点看了继承相关内容,已经记不得看了第几次这个内容,终于觉得自己好像懂了,特记录下来过程. js面向对象继承分为两大类,主要分为对象继承和非对象继承(拷贝继承) ...
- MFC 单文档 根据数据 绘图
以VS2015中创建SDI为例 选择生成的类为:C***View,基类为CView,***为项目名 在***Doc.h和***Doc.cpp中创建数据模型,在***View.cpp的OnDraw()中 ...
- POJ1904 King's Quest(完备匹配可行边:强连通分量)
题目大概就是说给一张二分图以及它的一个完备匹配,现在问X部的各个点可以与Y部那些些点匹配,使得X部其余点都能找到完备匹配. 枚举然后匹配,当然不行,会超时. 这题的解法是,在二分图基础上建一个有向图: ...
- 斑点检测(LoG,DoG) [上]
斑点检测(LoG,DoG) [上] 维基百科,LoG,DoG,DoH 在计算机视觉中,斑点检测是指在数字图像中找出和周围区域特性不同的区域,这些特性包括光照或颜色等.一般图像中斑点区域的像素特性相似甚 ...
- BZOJ4568 : [Scoi2016]幸运数字
树的点分治,每次求出重心后,求出重心到每个点路径上的数的线性基. 对于每个询问,只需要暴力合并两个线性基即可. 时间复杂度$O(60n\log n+60^2q)$. #include<cstdi ...
- Python for Informatics 第11章 正则表达式三(译)
注:文章原文为Dr. Charles Severance 的 <Python for Informatics>.文中代码用3.4版改写,并在本机测试通过. 11.2 用正则表达式抽取数据 ...
- 【HDU】4336 Card Collector
http://acm.hdu.edu.cn/showproblem.php?pid=4336 题意:n张卡片,每一次取一个盒子,盒子里装有卡片i的概率是p[i],求得到所有卡片所需要开的盒子的期望数( ...
- 【JAVA】LOG4J使用心得
一.LOG4J基础: 1.日志定义 简单的Log4j使用只需要导入下面的包就可以了 // import log4j packages import org.apache.log4j.Lo ...