收藏 分享(赏)

WCF通讯软件设计与实现.doc

上传人:春华秋实 文档编号:3291491 上传时间:2018-10-10 格式:DOC 页数:34 大小:361.50KB
下载 相关 举报
WCF通讯软件设计与实现.doc_第1页
第1页 / 共34页
WCF通讯软件设计与实现.doc_第2页
第2页 / 共34页
WCF通讯软件设计与实现.doc_第3页
第3页 / 共34页
WCF通讯软件设计与实现.doc_第4页
第4页 / 共34页
亲,该文档总共34页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

1、.页眉.页脚基于 WCF 的即时通讯软件的设计与实现摘要: 介绍了 Microsoft 用于构建分布式面向服务架构系统的新一代框架 WCF 的体系结构与技术要素,并通过开发一套即时通信软件展现了基于 WCF 构架开发分布式应用程序的编程步骤与技巧。关键词:WCF;SOA ;分布式;即时通信 一、前言自从在微软提出.NET 战略以来,就针对建立企业级的分布式应用先后推出了一系列产品和技术,包括:ASP.NET Web 服务、.NET Remoting、Message Queuing 以及 Enterprise Service 等。这些技术为基于微软技术的软件研发人员开发分布式应用提供了很大的便利

2、,同时也各自存在着一些不足。WCF(Windows Communication Foundation)作为微软基于SOA 所推出的.NET 平台下的新一代框架产品集成了现有技术的优点,代表了未来软件架构设计与开发的发展方向。因此,掌握并能在未来应用中合理运用 WCF 技术,对于程序员特别是基于微软技术开发的程序员而言是十分必要的。基于此,文章通过介绍一套即时通信软件的具体开发过程来展现基于 WCF 技术的分布式软件研发的基本步骤与高级技巧。二、WCF 概述对于一个好的分布式系统来讲,设计时应当考虑到异构性、开放性、安全性、可扩展性、故障处理、并发性以及透明性等问题。基于 SOAP 的 Web

3、Service 可以实现异构环境的互操作性,保证了跨平台的通信。利用 WSE(Web Service Enhancements)可以为ASMX 提供安全性的保证。.NET Remoting 具有丰富的扩展功能,可以创建定制的信道、格式化器和代理程序。Enterprise Service(COM+ )提供了对事务的支持,其中还包括分布式事务,可实现故障的恢复。MSMQ 可以支持异步调用、脱机连接、断点连接等功能,利用消息队列支持应用程序之间的消息传递。从功能角度来看,WCF 整合了 ASMX、.Net Remoting、Enterprise Service、WSE 以及 MSMQ 等现有技术的优

4、点,它提供了一种构建安全可靠的分布式面向服务系统的统一的框架模型,使软件研发人员在开发分布式应用时变得更加轻松。1. 面向服务既然 WCF 是一套面向服务的框架,服务自然便是 WCF 中最为重要的概念。服务是指暴露在外的一系列功能的集合,面向服务则是指一套构建“面向服务程序”的抽象原则以及最优方法。对于业务逻辑的理解,传统的编程方式认为应将业务逻辑封装为对象,该对象提供了与业务相关的一些功能;而基于 WCF 的程序设计却更多的是考虑如何提供服务以及消费服务。与面向组件服务程序类似,基于 SOA 的应用程序将服务封装到了单个逻辑程序当中,如图 1 所示。图 1 封装服务的 SOA 应用程序逻辑图

5、2. WCF 体系结构WCF 拥有一个非常灵活的分层体系结构,分布式应用程序可以使用高级 API 或者低级API 编写。高级 API 或者服务层可以用于调用方法和事件。服务层把这些高级的抽象代码.页眉.页脚转换为消息,以使用低级 API 上的信道和端口。图 2 中显示了 WCF 应用程序的各个层。图 2 WCF 体系结构图WCF 提供了对可靠性、事务性、并发管理、安全性以及实例激活等技术的有力支持,而这些支持均依赖于如图 3 所示的 WCF 构架。在客户端,分布式应用通过一个代理来转发对宿主端所提供服务的调用,而代理拥有和服务相同的操作接口,另外还有一些附加的代理管理方法。这也就意味着客户端从

6、来不会直接调用服务,即便这个服务就在本机的内存中。当客户端代理接收到来自客户端的调用请求后,它将消息通过信道链向下传递。每个信道都会执行相应的消息的调用前处理,例如对消息的编码、提供可靠的会话、对消息进行加密等。客户端的最后一个信道则是传输信道,根据配置的传输方式发送消息给宿主。在宿主端,消息同样通过信道链进行传输。与客户端信道相对应,宿主端信道也会对消息执行相应的宿主端的调用前处理,例如对消息的解码、提供会话管理、对消息进行解密等。宿主端的最后一个信道则负责将消息发送给消息分发器(Dispatcher),由分发器负责调用服务的实例。图 3 WCF 构架示意图3. WCF 的基本技术要素作为基

7、于 SOA 的一个框架产品,WCF 实际上是构建了一个在互联系统中实现各个应用程序之间通信的分布式框架。它使得系统构架师与开发人员在构建分布式系统时,能将更多的精力投入到与系统的业务逻辑本身的设计上来,而无需过多的考虑底层通信的实现及相关问题。WCF 最核心的部分是能够快捷的创建一个服务,一个 WCF 服务端框架由宿主、端点以及服务类三部分所组成,如图 4 所示。图 4 WCF 服务框架宿主(Host),即承载 WCF Service 运行的环境。可用的宿主环境包括:(1) 自承载方式:在控制台应用程序与基于 WinForm 的应用程序中都可以使用这种方式;(2) 系统服务方式:服务可以随着操

8、作系统的启动而自动启动;(3) IIS 方式:与 Web Services 的部署方式类似,由请求消息来激活服务,但只支持HTTP 方式的绑定;(4) WAS( Windows Process Activation Service)方式:这个宿主是 IIS7 的一部分,只有 Windows Vista 和 Windows Server 2008 提供默认支持,它支持几乎所有的通讯协议并提供了应用程序池、循环回收、空闲管理、身份管理、隔离等强大的功能。服务类(Service Class)是指一个标记了一些 WCF 特有的属性的类,它包含了对服务的业务逻辑的具体实现。端点(Endpoints)是

9、WCF 实现通信的核心要素,客户端和服务端都通过端点来交换消息,WCF 允许我们为服务添加多个绑定和端点。端点由地址(Address )、绑定(Binding )以及契约(Contract)三部分组成,如图 5 所示。在 WCF 中,类 ServiceEndpoint 代表了一个Endpoint,在类中包含的 EndpointAddress,Binding,ContractDescription 类型分别对应端点.页眉.页脚中的地址、绑定以及契约。 图 5 端点构成图地址:每个服务都会关联到一个唯一的地址,因此地址定位和唯一标志了一个端点,其主要提供了两个重要信息: 服务位置以及传送协议。在

10、WCF 中,地址由System.ServiceModel.EndpointAddress 对象来表示,其包括 URI、Identity、Headers 三个要素。绑定:绑定提供了一种可设置的方式来选择传输协议、消息编码、通讯模式、可靠性、安全性、事务传播以及交互方式等。例如在传输协议上可以选择HTTP/HTTPS、TCP 、P2P、IPC 甚至是 MSMQ 等方式。消息编码上可以选择使用纯文本方式来确保互操作能力,或者选择二进制编码来优化性能,或者使用 MTOM 来提高负载能力,甚至是自定义编码方式。 WCF 中提供了BasicHttpBinding、NetTcpBinding、NetPeer

11、TcpBinding 、NetNamedPipeBinding、WSHttpBinding、WSFederationHttpBinding、WSDualHttpBinding 、NetMsmqBinding 以及MsmqIntegrationBinding 九种标准类型的绑定。契约:契约是用来描述服务功能的一种平台中立的标准方式,WCF 所有服务都需要实现一个或多个契约。WCF 定义了四种类型的契约: (1) 服务契约(Service Contracts): 定义了客户端可以使用哪些服务操作。 (2) 数据契约(Data Contracts): 定义了服务传输的数据类型。 WCF 定义了一些隐

12、式数据契约,比如 int、string 等,但更多时候需要使用 DataContractAttribute 显式定义那些自定义类型数据的数据契约。 (3) 错误处理契约( Fault Contracts): 定义了服务引发的错误信息,以及如何将这些异常传递给客户端。 (4) 消息契约(Message Contracts ): 允许直接操控服务的消息内容和格式。一般情况下,应当用接口来定义服务契约,尽管我们也可以使用类。将服务契约定义为接口基于如下几点优点:(1) 便于契约的继承,不同根的类型可以自由实现相同的契约; (2) 同一服务类型可以同时实现多个契约; (3) 类似于接口隔离原则,可以随

13、时修改服务类型的实现而不影响其它实现; (4) 便于制定版本升级策略,新、旧版本的服务契约可以同时使用而互不影响。在 WCF 中,对于自承载的服务,端点的相关的信息可以有代码实现与配置文件两种定义方式。而对于 IIS 承载服务,端点的相关的信息一般定义在虚拟根目录下的Web.Config 文件中。一般来讲,使用配置文件来定义端点相关信息是更为灵活、更为推荐的一种方式,其可以在不修改代码、不重新发布系统的情况下对服务的地址、绑定和契约等参数进行修改(因为修改 config 类型文件的内容是不需要重新编译和重新部署的)。在下面的代码中具体说明了如何定义宿主端的端点相关信息。其中地址为http:/l

14、ocalhost:8080/HelloService,契约为 WCFServiceHello 命名空间下的 IHello 接口,绑定采用的是 WSHttpBinding 方式。值得注意的是,代码中的 HelloService 为相对地址,http:/localhost:8080/提供的是基址,当然去掉基址直接将 address 设为http:/localhost:8080/HelloService 也是可以的。代码中还添加了名为MyServiceTypeBehaviors 的行为配置,其将 serviceMetadata 节中的 httpGetEnabled 属性设为了 true,目的是为了自

15、动透过 HTTP-GET 发布服务的元数据。WCF 提供的另外一种发布元数据的方式是使用专门的 MEX 端点。.页眉.页脚在接下来的宿主代码中,只需要简单的创建 ServiceHost 类型的对象,并利用其实例方法 Open 启动服务应用程序即可,简要代码如下所示:using(ServiceHost host=new ServiceHost(typeof(WCFServiceHello.HelloWorld)Console.WriteLine(“HelloService has been started.“);host.Open();Console.ReadKey();客户端和服务之间通过消息

16、交换来完成方法调用和数据传递,而在 WCF 中定义了 3种消息交换模式,如图 6 所示。(1) OneWay:这种消息交换模式在调用方法后会立即返回而不需要等待服务端的消息返回。(2) Request/Reply:这种消息交换模式属于同步调用。在调用服务方法后需要等待服务端的消息返回。(3) Duplex:这种消息交换模式具有客户端与服务端双向通信的功能,同时它的实现还可以使消息交换具有异步回调的作用。图 6 WCF 中的 3 种消息交换模式在设置完宿主端端点之后,同样也必须为分布式应用程序定义客户端的端点,而且只有当客户端的端点与宿主端的某个端点相互匹配时,客户端的请求才能被宿主端所监听到。

17、如果服务提供了发布元数据,那么利用.NET Framework 3.0 SDK 所提供的 SvcUtil.exe 工具.页眉.页脚可以很轻松的自动生成与宿主端对应的客户端代理以及客户端配置文件。比如,运行宿主端应用程序,然后打开 Visual Studio 2005 命令提示符,键入 SvcUtil http:/localhost:8080,便可以在当前目录下得到客户端代理文件 HelloWorld.cs 与客户端配置文件 output.config。另外一种简便直观的可视化工具是 SDK 所附带的 SvcConfigEditor.exe(C:Program FilesMicrosoft SD

18、KsWindowsv6.0Abin 目录下,XP 系统),使用这个工具可以非常方便地创建或修改宿主端和客户端的配置文件。生成好客户端代理与配置文件后,在代码中直接使用客户端代理对象即可。using (HelloClient client = new HelloClient()client.Hello();Console.ReadKey();另外一种创建客户端代理的方式是使用 ChannelFactory 动态的来创建。虽然 WCF 提供了这种方式,但是在实际开发中并不推荐使用它,毕竟 ChannelFactory 直接依赖于契约,而这恰恰违背了 SOA 中边界隔离的原则。利用服务器端与客户端之

19、间的 Channel 来创建客户端代理的代码举例如下:ServiceEndpoint httpendpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(IHello), new WSHttpBinding(), new EndpointAddress(“http:/localhost:8080/HelloService“);using (ChannelFactory factory =new ChannelFactory(httpendpoint)IHello service = factory.CreateCh

20、annel();service.Hello();Console.ReadKey();三、软件的分析与设计软件主要的功能是初步实现基于 WCF 的局域网内的实时通信,以面向服务为指导思想将具体开发过程分为即时通信服务的设计与实现、宿主的设计与实现以及即时通信客户端的设计与实现三部分,使得应用程序具有较好的安全性、并发性、可扩展性以及可维护性。1. 服务的设计服务的设计包括了通用模块 Common.dll 的设计、服务契约 IChat 的定义与实现、客户端的回调接口 IChatCallback 的定义三个部分。通用模块 Common.dll 主要包括对聊天者类型 Person 的定义,其包括 na

21、me(聊天者的名称或代号)以及 ImageURL(聊天者选择的头像图片的存储路径)两个私有字段及相应的属性访问器。由于 Person 类型是自定义数据类型,因此必须加上 DataContractAttribute 来显式定义它。Person 类的实现核心代码如下:DataContractpublic class Person private string imageURL;private string name;public Person(string imageURL, string name).页眉.页脚this.imageURL = imageURL;this.name = name;D

22、ataMemberpublic string ImageURLget return imageURL; setimageURL = value;DataMemberpublic string Nameget return name; setname = value;服务契约 IChat 的定义中主要包括 Join、Say、Whisper 以及 Leave 四个基本方法。其中 Join 表示:进入聊天;Say 表示向所有用户广播消息;Whisper 表示对指定的用户发送消息;Leave 表示离开聊天。需添加 ServiceContract 属性将 IChat 接口标记为服务契约,由于要实现客户端

23、与服务端双向通信的功能,因此还必须设置 CallbackContract 参数,其参数 IChatCallback 为Duplex 模式下的客户端回调类型。对于 IChat 接口中的方法来讲,必须标记 OperationContract 属性。其中 IsOneWay 指示在该消息交换模式下调用方法后是否会立即返回而不需要等待服务端的消息返回;IsInitiating指示服务方法是否启动一个 Session;IsTerminating 指示服务方法调用完成是否结束 Session。IChat 的定义代码如下:ServiceContract(SessionMode = SessionMode.Re

24、quired, CallbackContract = typeof(IChatCallback)interface IChatOperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)void Say(string msg);OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)void Whisper(string to, string msg);.页眉.页脚OperationContrac

25、t(IsOneWay = false, IsInitiating = true, IsTerminating = false)Person Join(Person name);OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)void Leave();客户端的回调接口 IChatCallback 的定义包括了分别对应 Join、Say、Whisper 以及Leave 的 UserEnter、Receive、ReceiveWhisper 以及 UserLeave 四个基本方法。UserEnt

26、er 表示当有新聊天用户加入时所有聊天用户接收到一个相应的通知;Receive 表示接收用户广播的消息;ReceiveWhisper 表示接收相关用户发来的消息;UserLeave 表示当有聊天用户离开时所有聊天用户接收一个相应的通知。注意在接口定义中,每个服务方法的消息转换模式均设置为 One-Way。此外,回调接口是被本地调用,因此不需要定义ServiceContract属性。回调接口 IChatCallback 的定义的代码如下:interface IChatCallbackOperationContract(IsOneWay = true)void Receive(Person sen

27、der, string message);OperationContract(IsOneWay = true)void ReceiveWhisper(Person sender, string message);OperationContract(IsOneWay = true)void UserEnter(Person person);OperationContract(IsOneWay = true)void UserLeave(Person person);定义了服务契约以及客户端回调接口之后,仍需要定义一些消息类型用作客户端与服务之间的交互,具体代码及解释如下:/ 定义说明消息类型的枚

28、举类型 MessageTypepublic enum MessageType Receive, UserEnter, UserLeave, ReceiveWhisper ;/ ChatEventArgs 继承至 EventArgs,作为传递的消息参数,/其包括消息的类型 msgType、消息发送者 person 以及消息的主体内容 messagepublic class ChatEventArgs : EventArgspublic MessageType msgType;public Person person;public string message;最后是对服务契约中 IChat 接口

29、的实现类 ChatService 的设计。值得注意的是ChatService 继承了 IChat,不再需要像服务契约 IChat 一样添加 ServiceContract 属性。另外设置了其 ServiceBehavior 属性的 InstanceContextMode 参数来决定实例化方式(PerSession方式或者 PerCall 方式)。PerSession 表明服务对象的生命周期存活于一个会话期间,在同.页眉.页脚一个会话期间对于服务的不同操作的调用都会施加到同一个客户端代理类型的对象上;PerCall 则表示服务对象是在方法被调用时创建,结束后即被销毁。ServiceBehavio

30、r.ConcurrencyMode 参数的设置则用于控制具体服务对象的并发行为,其包括三种行为:Single: 为默认方式。服务实例是单线程的,不接受重入调用 (reentrant calls)。也就是说对于同一个服务实例的多个调用必须排队,直到上一次调用完成后才能继续。Reentrant: 和 Single 一样,也是单线程的,但能接受重入调用,至于针对同一服务对象的多个调用仍然需要排队。因为在 Single 模式下,当方法调用另外一个服务时,方法会阻塞,直到所调用的服务完成。如果方法不能重入,那么调用方会因无法接受所调用服务的返回消息,无法解除阻塞状态而陷入死锁。Reentrant 模式解

31、决了这种不足。Multiple: 允许多个客户端同时调用服务方法。不再有锁的问题,不再提供同步保障。因此使用该模式时,必须自行提供多线程同步机制(比如在本例中给 static 类型的 syncObj对象加锁)来保证数据成员的读写安全。类 ChatService 的实现代码及解析如下:ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,ConcurrencyMode = ConcurrencyMode.Multiple)public class ChatService : IChat/用于保障多线程同步而设置的

32、对象private static Object syncObj = new Object();/客户端的回调接口对象IChatCallback callback = null;/用于广播事件的委托public delegate void ChatEventHandler(object sender, ChatEventArgs e);public static event ChatEventHandler ChatEvent;private ChatEventHandler myEventHandler = null;/利用字典对象 chatters 来保存聊天者对象以及绑定的对应事件委托st

33、atic Dictionary chatters =new Dictionary();/当前聊天者对象private Person person;/ 判断具有相应名字的聊天者是否存在于字典对象中private bool checkIfPersonExists(string name)foreach (Person p in chatters.Keys)if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)return true;return false;/ 在字典对象中搜索判断其中是否包含了相应名字的聊天者,.页眉.页脚/ 如果有

34、则返回其对应的委托 ChatEventHandler;否则返回空private ChatEventHandler getPersonHandler(string name)foreach (Person p in chatters.Keys)/不区分大小写if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)ChatEventHandler chatTo = null;chatters.TryGetValue(p, out chatTo);return chatTo;return null;/ 在字典对象中搜索判断其中是否包含了相

35、应名字的聊天者,/ 如果有则返回聊天者对象;否则返回空private Person getPerson(string name)foreach (Person p in chatters.Keys) if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)return p;return null;/ 加入聊天,如果字典对象 chatters 中没有同名的聊天者public Person Join(Person person) bool userAdded = false;/创建新的 ChatEventHandler 类型委托,其指向

36、 MyEventHandler()方法myEventHandler = new ChatEventHandler(MyEventHandler);/执行关键区域,判断是否存在同名聊天者,/如果不存在则向字典中添加该对象并将 MyEventHandler 委托作为其 value,/以待后面来触发lock (syncObj)if (!checkIfPersonExists(person.Name) chatters.Add(person, MyEventHandler);.页眉.页脚userAdded = true;/如果新的聊天者添加成功,获得一个回调的实例来创建一个消息,/并将其广播给其他的聊

37、天者/读取字典 chatters 的所有聊天者对象并返回一个包含了所有对象的列表if (userAdded)callback = OperationContext.Current.GetCallbackChannel();ChatEventArgs e = new ChatEventArgs();e.msgType = MessageType.UserEnter;e.person = this.person;/广播有新聊天者加入的消息BroadcastMessage(e);/将新加入聊天者对象的委托加到全局的多播委托上ChatEvent += myEventHandler;Person lis

38、t = new Personchatters.Count;/执行关键区域,将字典 chatters 的所有聊天者对象/拷贝至一个聊天者列表上,用于方法返回lock (syncObj)chatters.Keys.CopyTo(list, 0);return list;elsereturn null;/ 广播当前聊天者输入的消息给所有聊天者public void Say(string msg)ChatEventArgs e = new ChatEventArgs();e.msgType = MessageType.Receive;e.person = this.person;e.message =

39、 msg;BroadcastMessage(e);/在字典对象 chatters 中查找具有相应名称的聊天者对象,/并异步地触发其对应的 ChatEventHandle 委托public void Whisper(string to, string msg)ChatEventArgs e = new ChatEventArgs();.页眉.页脚e.msgType = MessageType.ReceiveWhisper;e.person = this.person;e.message = msg;tryChatEventHandler chatterTo;/执行关键区域,获取具有相应名称的聊天

40、者对象在 chatters 中所对应的委托lock (syncObj)chatterTo = getPersonHandler(to);if (chatterTo = null)throw new KeyNotFoundException(“The person whos name is “ + to + “ could not be found“); /异步执行委托chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);catch (KeyNotFoundException)/ 当聊天者离开时,从字典 chatter

41、s 中移除包含该对象的项。/ 并从全局的多播委托上移除该对象对应的委托public void Leave()if (this.person = null)return;/获得该聊天者对象所对应的 ChatEventHandler 委托ChatEventHandler chatterToRemove = getPersonHandler(this.person.Name);/执行关键区域,从从字典 chatters 中移除包含该聊天者对象的项。lock (syncObj)chatters.Remove(this.person);/从全局的多播委托上移除该对象对应的委托ChatEvent -= c

42、hatterToRemove;ChatEventArgs e = new ChatEventArgs();e.msgType = MessageType.UserLeave;e.person = this.person;this.person = null;/将消息广播给其他聊天者BroadcastMessage(e);.页眉.页脚/ 当 chatters 中的聊天者对象所对应的 ChatEventHandler 委托被触发时,/ MyEventHandler 方法将执行。/ 该方法通过检查传递过来的 ChatEventArgs 参数类型来执行相应的客户端/的回调接口中的方法。private

43、void MyEventHandler(object sender, ChatEventArgs e)tryswitch (e.msgType)case MessageType.Receive:callback.Receive(e.person, e.message);break;case MessageType.ReceiveWhisper:callback.ReceiveWhisper(e.person, e.message);break;case MessageType.UserEnter:callback.UserEnter(e.person);break;case MessageTy

44、pe.UserLeave:callback.UserLeave(e.person);break;catchLeave();/ 异步地触发字典 chatters 中所有聊天者对象所对应的 ChatEventHandler 委托。/ BeginInvoke 方法可启动异步调用。第一个参数是一个 AsyncCallback 委托,/该委托引用在异步调用完成时要调用的方法。/ 第二个参数是一个用户定义的对象,该对象可向回调方法传递信息。/BeginInvoke 立即返回,不等待异步调用完成。/BeginInvoke 会返回 IAsyncResult,这个结果可用于监视异步调用进度。private vo

45、id BroadcastMessage(ChatEventArgs e)ChatEventHandler temp = ChatEvent;if (temp != null)foreach (ChatEventHandler handler in temp.GetInvocationList()handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);.页眉.页脚/ EndInvoke 方法检索异步调用的结果。/调用 BeginInvoke 后可随时调用 EndInvoke 方法;/如果异步调用尚未完成,EndInvoke

46、将一直阻止调用线程,/直到异步调用完成后才允许调用线程执行。private void EndAsync(IAsyncResult ar)ChatEventHandler d = null;try/System.Runtime.Remoting.Messaging.AsyncResult 封装了异步委托上/的异步操作的结果。/AsyncResult.AsyncDelegate 属性可用于获取在其上调用/异步调用的委托对象。 System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messagin

47、g.AsyncResult)ar;d = (ChatEventHandler)asres.AsyncDelegate);d.EndInvoke(ar);catchChatEvent -= d;2. 宿主端的设计设计好服务之后,宿主端的设计相对简单,只是一个基于控制台的应用程序。首先建立一个 ServiceHost 类型的对象 host,然后调用 ServiceHost 类型的实例方法 Open 即可启动宿主监听。需要注意的是在程序末尾应当调用 Abort 以及 Close 将资源释放掉,或者利用using 语句来自动释放所占用的资源。下面是宿主端的实现代码以及配置文件内容:static voi

48、d Main(string args)Uri uri = new Uri(ConfigurationManager.AppSettings“addr“);ServiceHost host = new ServiceHost(typeof(Chatters.ChatService), uri);host.Open();Console.WriteLine(“Chat service Host now listening on Endpoint 0“, uri.ToString();Console.WriteLine(“Press ENTER to stop chat service.“);Cons

49、ole.ReadLine();host.Abort();host.Close(); .页眉.页脚配置文件中,地址为 net.tcp:/localhost:10001/chatservice;绑定为 netTcpBinding,并且提供可靠性传输、不保障安全性,发送超时时间限定为 1 秒;契约为 Chatters.IChat 接口。另外设置了名称为 DuplexBinding 的绑定配置,其对服务的分流作了限制,并发的 Session个数的上限为 10000。3. 客户端的设计客户端的设计主要包括实现客户端代理与配置文件、客户端的回调接口契约的实现以及客户端登录、主窗体、聊天窗体界面的设计与代码逻辑实现这三部分。表示客户端的各个对象之间行为关系的序列图如图 7 所示。图 7 客户端应用的序列图首先是利用 svcutil.exe 工具自动

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 学术论文 > 毕业论文

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报