WCF学习之旅—请求与答复模式和单向模式(十九)

WCF学习之旅—HTTP双工模式(二十)

五、TCP双工模式

上一篇文章中我们学习了HTTP的双工模式,我们今天就学习一下TCP的双工模式。

在一个基于面向服务的分布式环境中,借助一个标准的、平台无关的通信协议,使各个服务通过SOAP Message实现相互之间的交互。这个交互的过程实际上就是信息交换的过程。WCF支持不同形式的信息交换,我们把这称之为信息交换模式(Message Exchange Pattern(简称MEP),下同), 常见的MEP包括: 请求/答复,单向模式和双工模式。通过采用双工的MEP,我们可以实现在服务端调用客户端的操作。虽然WCF为我们实现底层的通信细节,使得我们把精力转移到业务逻辑的实现,进行与通信协议无关的编程,但是对通信协议的理解有利于我们根据所处的具体环境选择一个合适的通信协议。说到通信协议, WCF 经常使用的是以下4个:Http,TCP,Named Pipe,MSMQ。

我们用上一文章( WCF学习之旅—HTTP双工模式(二十))中的示例,进行一下修改,变成一个TCP双向通信的WCF服务应用程序。下面直接上代码。


1
Contract

 

using Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace Contracts
{ // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IBookService”。
[ServiceContract(CallbackContract = typeof(ICallback))]
public interface IBookService
{ /// <summary>
/// 请求与答复模式,默认模式
/// </summary>
/// <param name="Id">书籍ID</param>
/// <returns></returns>
[OperationContract]
string GetBook(string Id);
/// <summary> /// 单工模式,显示名称
/// </summary>
/// <param name="name">书籍名称</param>
[OperationContract(IsOneWay = true)]
void ShowName(string name);
/// <summary>
/// 双工模式,显示名称
/// </summary>
/// <param name="name">书籍名称</param>
[OperationContract(IsOneWay = true)]
void DisplayName(string name); } } using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace Contracts
{ public interface ICallback
{ [OperationContract(IsOneWay = true)]
void DisplayResult(string result); }
}

 

2WcfServiceLib

using Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace WcfServiceLib
{ // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“BookService”。 // 注意: 为了启动 WCF 测试客户端以测试此服务,请在解决方案资源管理器中选择 BookService.svc 或 BookService.svc.cs,然后开始调试。
public class BookService : IBookService
{ /// <summary>
/// 请求与答复模式,默认模式
/// </summary>
/// <param name="Id">书籍ID</param>
/// <returns></returns>
public string GetBook(string Id)
{ System.Threading.Thread.Sleep();
int bookId = Convert.ToInt32(Id);
Books book = SetBook(bookId);
string xml = XMLHelper.ToXML<Books>(book);
return xml; } public Books SetBook(int Id)
{ Books book = new Books();
book.BookID = Id;
book.AuthorID = ;
book.Category = "IBM";
book.Price = 39.99M;
book.Numberofcopies = ;
book.Name = "DB2数据库性能调整和优";
book.PublishDate = new DateTime(, , );
return book;
} /// <summary>
/// 单工模式,显示名称
/// </summary>
/// <param name="name">名称</param>
public void ShowName(string name)
{ string result = string.Format("书籍名称:{0},日期时间{1}", name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
Console.WriteLine("\r\n" + result); } /// <summary>
/// 双工模式,回调显示结果
/// </summary>
/// <param name="name">名称</param>
public void DisplayName(string name)
{
string result=string.Format("书籍名称:{0},日期时间{1}", name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
Console.WriteLine("\r\n" + result);
ICallback call = OperationContext.Current.GetCallbackChannel<ICallback>();
call.DisplayResult("回调客户端---"+result); } } }

在服务端,通过OperationContext.Current.GetCallbackChannel来获得客户端指定的CallbackContext
实例,进而调用客户端的操作。

3Hosting

宿主配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> </startup>
<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true" logKnownPii="false" logMalformedMessages="true"
logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" />
<endToEndTracing propagateActivity="true" activityTracing="true"
messageFlowTracing="true" />
</diagnostics> <services>
<service name="WcfServiceLib.BookService">
<endpoint address="net.tcp://127.0.0.1:9999/BookService" binding="netTcpBinding"
contract="Contracts.IBookService" /> </service>
</services>
</system.serviceModel>
</configuration>

我们通过netTcpBinding来模拟基于TCP的双向通信。代码如下:

using Contracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks;
using WcfServiceLib; namespace ConsoleHosting
{ class Program
{ static void Main(string[] args)
{
Console.WriteLine("输入启动方式,C--Code A -- App.config方式!");
string key = Console.ReadLine();
switch (key)
{
case "C":
StartByCode();
break;
case "A":
StartByConfig();
break;
default:
Console.WriteLine("没有选择启动方式,使用默认方式");
StartByCode();
break; }
} private static void StartByCode()
{ //创建宿主的基地址
Uri baseAddress = new Uri("http://localhost:8080/BookService");
//创建宿主
using (ServiceHost host = new ServiceHost(typeof(BookService), baseAddress))
{ //向宿主中添加终结点
host.AddServiceEndpoint(typeof(IBookService), new WSDualHttpBinding(), baseAddress);
if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null) {
//将HttpGetEnabled属性设置为true
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
behavior.HttpGetUrl = baseAddress;
//将行为添加到Behaviors中 host.Description.Behaviors.Add(behavior);
//打开宿主 host.Opened += delegate
{
Console.WriteLine("BookService控制台程序寄宿已经启动,HTTP监听已启动....,按任意键终止服务!");
}; host.Open();
//print endpoint information Console.ForegroundColor = ConsoleColor.Yellow;
foreach (ServiceEndpoint se in host.Description.Endpoints)
{
Console.WriteLine("[终结点]: {0}\r\n\t[A-地址]: {1} \r\n\t [B-绑定]: {2} \r\n\t [C-协定]: {3}",
se.Name, se.Address, se.Binding.Name, se.Contract.Name);
}
Console.Read();
}
} } private static void StartByConfig()
{
using (ServiceHost host = new ServiceHost(typeof(BookService)))
{
host.Opened += delegate
{ Console.WriteLine("BookService控制台程序寄宿已经启动,TCP监听已启动....,按任意键终止服务!");
}; host.Open();
//print endpoint information Console.ForegroundColor = ConsoleColor.Yellow;
foreach (ServiceEndpoint se in host.Description.Endpoints)
{
Console.WriteLine("[终结点]: {0}\r\n\t[A-地址]: {1} \r\n\t [B-绑定]: {2} \r\n\t [C-协定]: {3}",
se.Name, se.Address, se.Binding.Name, se.Contract.Name); } Console.Read();
} } } }

4.客户端:

配置文件中的信息进行修改:

  <system.serviceModel>
<client>
<endpoint address="net.tcp://localhost:9999/BookService" binding="netTcpBinding" bindingConfiguration="" contract="Contracts.IBookService" name="BookServiceEndpoint" />
</client>
</system.serviceModel>

接下来实现对双工服务的调用,下面是相关的配置和托管程序。在服务调用程序中,通过 DuplexChannelFactory<TChannel>创建服务代理对 象,DuplexChannelFactory<TChannel>和ChannelFactory<TChannel>的功能
都是一个服务代理对象的创建工厂,不过DuplexChannelFactory<TChannel>专门用于基于双工通信的服务代理的创
建。在创建DuplexChannelFactory<TChannel>之前,先创建回调对象,并通过InstanceContext对回 调对象进行包装。代码如下:

private void btnTcpDuplex_Click(object sender, EventArgs e)
{ DuplexChannelFactory<IBookService> channelFactory = new DuplexChannelFactory<IBookService>(instanceContext, "BookServiceEndpoint");
IBookService client = channelFactory.CreateChannel(); //在BookCallBack对象的mainThread(委托)对象上搭载两个方法,在线程中调用mainThread对象时相当于调用了这两个方法。 textBox1.Text += string.Format("开始调用wcf服务:{0}\r\n\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); client.DisplayName("TCP---科学可以这样看丛书"); textBox1.Text += string.Format("\r\n\r\n调用结束:{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
}

在创建 DuplexChannelFactory< IBookService >中,指定了Callback Context Instance: 一个实现了Callback
Contract的BookCallBack对象。该对象在Service中通过
OperationContext.Current.GetCallbackChannel<ICallback>()获得。

通过运行程序之后的结果如下图:

 


  2. 基于Http的双向通讯V.S.基于TCP的双向通讯

由于Http和TCP在各自协议上的差异,他们实现双向通信的发式是不同的。

Http是一个应用层的协议,它的主要特征就是无连接和无状态。它采用传统的“请求/回复”的方式进行通信,客户端发送Http
Request请求服务端的某个资源,服务端接收到该Http Request, 回发对应的Http Response。当客户端接收到对应的Response,该连接就会关闭。也就是说客户端和服务端的 连接仅仅维持在发送Request到接收到Response这一段时间内。同时,每次基于Http的 连接是相互独立,互不相干的,当前连接无法获得上一次连接的状态。为了保存调用的的状态信 息,ASP.NET通过把状态信息保存在服务端的Session之中,具体的做法是:ASP.NET为每个Session创建一个
Unique ID,与之关联一个HttpSessionState对象,并把状态信息保存在内存中或者持久的存储介质(比如SQL
Server)中。而WCF则采用另外的方式实现对Session的支持:每个Session关联到某个Service
Instance上。

我们来讲一下HTTP双向通信的过程,当客户端通过HTTP请求调用WCF服务之前,会有一个终结点在客户端被创建,用于监听服务端对它的Request。客户端对 WCF服务的调用会建立一个客户端到服务端的连接,当WCF服务在执行操作过程中需要回调对应的客户端,实际上会建立另一个服务端到客户端的Http 连接。虽然我们时候说WCF为支持双向通信提供一个双工通道,实际上这个双工通道是由两个HTTP连接组成的。

再来看一下TCP的双向通信的过程,对于TCP传输层协议,它则是一个基于连接的协议,在正式进行数据传输的之前,必须要在客户端和服务端之间建立一个连接,连接的建立通过经典的“3次握手”来实现。TCP天生就具有双工的特性,也就是说当连接 被创建之后,从客户端到服务端,和从服务端到客户端的数据传递都可以利用同一个连接来实现。对于WCF中的双向通信,客户端调用服务端,服务端回调客户端的操作使用的都是同一个连接、同一个通道。所以基于TCP的双工通信模式才是真正意义上的双工通信模式。

WCF学习之旅—TCP双工模式(二十一)的更多相关文章

  1. WCF学习之旅—HTTP双工模式(二十)

    WCF学习之旅—请求与答复模式和单向模式(十九) 四.HTTP双工模式 双工模式建立在上文所实现的两种模式的基础之上,实现客户端与服务端相互调用:前面介绍的两种方法只是在客户端调用服务端的方法,然后服 ...

  2. WCF学习之旅—TcpTrace工具(二十六)

    止文(WCF学习之旅—TcpTrace工具(二十五))介绍了关于TcpTrance的一种使用方式,接下来介绍第二种使用方式. 三.通过ListenUri实现基于tcpTracer的消息路由 对于路由的 ...

  3. WCF学习之旅—TcpTrace工具(二十五)

    前面的几篇文章,我们学习了怎么开发WCF应用程序与服务,也学习了如何进行WCF的配置.对于Web Service与WCF服务应用,服务端与客户端的通信是通过收发SOAP Message进行,我们如何有 ...

  4. WCF学习之旅—WCF服务的WAS寄宿(十二)

    上接    WCF学习之旅—WCF服务部署到IIS7.5(九) WCF学习之旅—WCF服务部署到应用程序(十) WCF学习之旅—WCF服务的Windows 服务程序寄宿(十一) 八.WAS宿主 IIS ...

  5. WCF学习之旅——第一个WCF示例(二)

    第四步:通过自我寄宿的方式寄宿服务 WCF服务需要依存一个运行着的进程(宿主),服务寄宿就是为服务指定一个宿主的过程.WCF是一个基于消息的通信框架,采用基于终结点(Endpoint)的通信手段. 终 ...

  6. WCF学习之旅—第三个示例之二(二十八)

    上接WCF学习之旅—第三个示例之一(二十七) 五.在项目BookMgr.Model创建实体类数据 第一步,安装Entity Framework 1)  使用NuGet下载最新版的Entity Fram ...

  7. WCF学习之旅—第三个示例之三(二十九)

    上接WCF学习之旅—第三个示例之一(二十七) WCF学习之旅—第三个示例之二(二十八) 在上一篇文章中我们创建了实体对象与接口协定,在这一篇文章中我们来学习如何创建WCF的服务端代码.具体步骤见下面. ...

  8. WCF学习之旅—实现支持REST客户端应用(二十四)

    WCF学习之旅—实现REST服务(二十二) WCF学习之旅—实现支持REST服务端应用(二十三) 在上二篇文章中简单介绍了一下RestFul与WCF支持RestFul所提供的方法,及创建一个支持RES ...

  9. WCF学习之旅—实现支持REST服务端应用(二十三)

    在上一篇(WCF学习之旅—实现REST服务(二十二))文章中简单介绍了一下RestFul与WCF支持RestFul所提供的方法,本文讲解一下如何创建一个支持REST的WCF服务端程序. 四.在WCF中 ...

随机推荐

  1. JavaScript之父Brendan Eich,Clojure 创建者Rich Hickey,Python创建者Van Rossum等编程大牛对程序员的职业建议

    软件开发是现时很火的职业.据美国劳动局发布的一项统计数据显示,从2014年至2024年,美国就业市场对开发人员的需求量将增长17%,而这个增长率比起所有职业的平均需求量高出了7%.很多人年轻人会选择编 ...

  2. 配置android sdk 环境

    1:下载adnroid sdk安装包 官方下载地址无法打开,没有vpn,使用下面这个地址下载,地址:http://www.android-studio.org/

  3. 【疯狂造轮子-iOS】JSON转Model系列之二

    [疯狂造轮子-iOS]JSON转Model系列之二 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇<[疯狂造轮子-iOS]JSON转Model系列之一> ...

  4. 【探索】利用 canvas 实现数据压缩

    前言 HTTP 支持 GZip 压缩,可节省不少传输资源.但遗憾的是,只有下载才有,上传并不支持.如果上传也能压缩,那就完美了.特别适合大量文本提交的场合,比如博客园,就是很好的例子. 虽然标准不支持 ...

  5. 【流量劫持】躲避 HSTS 的 HTTPS 劫持

    前言 HSTS 的出现,对 HTTPS 劫持带来莫大的挑战. 不过,HSTS 也不是万能的,它只能解决 SSLStrip 这类劫持方式.但仔细想想,SSLStrip 这种算劫持吗? 劫持 vs 钓鱼 ...

  6. ASP.NET Core 折腾笔记一

    前言: 在ASP.NET Core 1.0时,曾折腾过一次,后因发现不了System.Data而停止. 更因VS2015提示过期Delete掉VS了,其实主要还是笔记本的硬盘空间吃紧. 快双十一了,本 ...

  7. git克隆项目到本地&&全局安装依赖项目&&安装依赖包&&启动服务

     一.安装本地开发环境 1.安装本项目 在需要保存到本地的项目的文件夹,进入到文件夹里点击右键,bash here,出现下图: 2.安装依赖项目  3.安装依赖包(进入到命令行) # 安装依赖包 $ ...

  8. 一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)

    在目前的软件项目中,都会较多的使用到对文档的操作,用于记录和统计相关业务信息.由于系统自身提供了对文档的相关操作,所以在一定程度上极大的简化了软件使用者的工作量. 在.NET项目中如果用户提出了相关文 ...

  9. JavaScript实现DOM对象选择器

    目的: 根据传入的选择器类型选出第一个符合的DOM对象. ①可以通过id获取DOM对象,例如 $("#adom"); ②可以通过tagName获取DOM对象,例如 $(" ...

  10. ASP.NET MVC原理

    仅此一文让你明白ASP.NET MVC原理   ASP.NET MVC由以下两个核心组成部分构成: 一个名为UrlRoutingModule的自定义HttpModule,用来解析Controller与 ...