客户端应用程序必须创建、配置和使用 WCF 客户端或通道对象来与服务通信。 WCF 客户端概述主题概述了创建基本客户端和通道对象和使用它们所涉及的对象和步骤。
本主题深入介绍了客户端应用程序以及客户端和通道对象的一些相关问题,根据你的实际情况,这些信息可能会很有帮助。
概述
本主题介绍与以下内容相关的行为和问题:
通道和会话生存期。
处理异常。
了解阻碍问题。
以交互方式初始化通道。
通道和会话生命周期
Windows Communication Foundation (WCF) 应用程序包括两类通道:数据报通道和会话通道。
数据报通道是一个通道,其中所有消息都无关。 使用数据报通道时,如果输入或输出作失败,则下一个作通常不受影响,并且可以重复使用同一通道。 因此,数据报通道通常不会出错。
与其相反,会话通道是与另一个终结点有连接的通道。 一端会话中的消息始终与另一端的同一会话关联。 此外,会话中的两名参与者都必须同意他们达成了会话的所有要求,才能将该会话视为成功。 如果他们无法商定,则会话通道可能会出错。
通过显式或隐式调用第一个操作来打开客户端。
注释
尝试显式检测发生故障的带有会话的通道通常并不有用,因为收到通知的时间取决于会话实现。 例如,因为 System.ServiceModel.NetTcpBinding(禁用了可靠会话)表现了 TCP 连接会话的状态,所以,如果您在服务或客户端上侦听 ICommunicationObject.Faulted 事件,则在出现网络故障时,您可能会很快得到通知。 但是,通过启用 System.ServiceModel.Channels.ReliableSessionBindingElement 的绑定建立的可靠会话旨在使服务免受小型网络故障的影响。 如果会话可以在合理的时间段内重新建立,则为可靠会话配置的同一绑定在中断持续更长时间之前可能不会出错。
大多数系统提供的绑定(向应用程序层公开通道)默认使用会话,但 System.ServiceModel.BasicHttpBinding 不会这样做。 有关详细信息,请参阅 “使用会话”。
正确使用会话
会话提供了一种方法来了解整个消息交换是否已完成,以及双方是否认为它成功。 建议让调用应用程序在一个 try 块内打开、使用和关闭通道。 如果会话通道处于打开状态,并且 ICommunicationObject.Close 调用方法一次,并且该调用成功返回,则会话成功。 在此情况下,成功意味着绑定所指定的所有传递保证都得到满足,并且另一方在调用 ICommunicationObject.Abort 之前没有对通道调用 Close。
以下部分提供了此客户端方法的示例。
处理异常
在客户端应用程序中处理异常非常简单。 如果在一个 try 块内打开、使用并关闭通道,则除非引发了异常,否则对话都是成功的。 通常情况下,如果引发了异常,则会中止对话。
注释
不建议对using
语句的使用(在 Visual Basic 中为Using
)。 这是因为 using
语句的末尾可能会引发异常,且此异常可能掩盖其他您需要了解的重要异常。 有关详细信息,请参阅 使用 Close 和 Abort 释放 WCF 客户端资源。
下面的代码示例演示了使用 try/catch 块而不是 using
语句的建议客户端模式。
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Microsoft.WCF.Documentation;
public class Client
{
public static void Main()
{
// Picks up configuration from the config file.
SampleServiceClient wcfClient = new SampleServiceClient();
try
{
// Making calls.
Console.WriteLine("Enter the greeting to send: ");
string greeting = Console.ReadLine();
Console.WriteLine("The service responded: " + wcfClient.SampleMethod(greeting));
Console.WriteLine("Press ENTER to exit:");
Console.ReadLine();
// Done with service.
wcfClient.Close();
Console.WriteLine("Done!");
}
catch (TimeoutException timeProblem)
{
Console.WriteLine("The service operation timed out. " + timeProblem.Message);
Console.ReadLine();
wcfClient.Abort();
}
catch (FaultException<GreetingFault> greetingFault)
{
Console.WriteLine(greetingFault.Detail.Message);
Console.ReadLine();
wcfClient.Abort();
}
catch (FaultException unknownFault)
{
Console.WriteLine("An unknown exception was received. " + unknownFault.Message);
Console.ReadLine();
wcfClient.Abort();
}
catch (CommunicationException commProblem)
{
Console.WriteLine("There was a communication problem. " + commProblem.Message + commProblem.StackTrace);
Console.ReadLine();
wcfClient.Abort();
}
}
}
Imports System.ServiceModel
Imports System.ServiceModel.Channels
Imports Microsoft.WCF.Documentation
Public Class Client
Public Shared Sub Main()
' Picks up configuration from the config file.
Dim wcfClient As New SampleServiceClient()
Try
' Making calls.
Console.WriteLine("Enter the greeting to send: ")
Dim greeting As String = Console.ReadLine()
Console.WriteLine("The service responded: " & wcfClient.SampleMethod(greeting))
Console.WriteLine("Press ENTER to exit:")
Console.ReadLine()
' Done with service.
wcfClient.Close()
Console.WriteLine("Done!")
Catch timeProblem As TimeoutException
Console.WriteLine("The service operation timed out. " & timeProblem.Message)
Console.ReadLine()
wcfClient.Abort()
Catch greetingFault As FaultException(Of GreetingFault)
Console.WriteLine(greetingFault.Detail.Message)
Console.ReadLine()
wcfClient.Abort()
Catch unknownFault As FaultException
Console.WriteLine("An unknown exception was received. " & unknownFault.Message)
Console.ReadLine()
wcfClient.Abort()
Catch commProblem As CommunicationException
Console.WriteLine("There was a communication problem. " & commProblem.Message + commProblem.StackTrace)
Console.ReadLine()
wcfClient.Abort()
End Try
End Sub
End Class
注释
检查 ICommunicationObject.State 属性的值是一个争用条件,建议不要使用此检查来确定是重用还是关闭通道。
即使在关闭数据报通道时出现异常,这些通道也从来不会出错。 另外,使用安全对话但未能通过身份验证的非双工客户端通常会引发 System.ServiceModel.Security.MessageSecurityException。 但是,与此不同的是,如果使用安全对话的双工客户端未能通过身份验证,该客户端会收到 System.TimeoutException。
有关在应用程序级别处理错误信息的详细信息,请参阅 “在协定和服务中指定和处理错误”。 预期异常 描述预期的异常,并演示如何处理这些异常。 有关如何在开发通道时处理错误的详细信息,请参阅 处理异常和故障。
客户端阻塞和性能
当应用程序同步调用请求-答复操作时,客户端会阻塞,直到接收到返回值或引发异常(例如 System.TimeoutException)为止。 此行为类似于本地行为。 当应用程序同步调用 WCF 客户端对象或通道上的操作时,客户端不会返回,直到通道层可以将数据写入网络或引发异常。 虽然单向消息交换模式(通过将操作标记为OperationContractAttribute.IsOneWaytrue
进行指定)可以使某些客户端更具响应性,但根据绑定方式和已发送消息的情况,单向操作也可能会阻塞。 单向操作只涉及消息交换,仅此而已。 有关详细信息,请参阅 One-Way 服务。
无论消息交换模式是什么,大型数据区块都可能会降低客户端处理速度。 若要了解如何处理这些问题,请参阅 大型数据和流式处理。
如果您的应用程序在操作完成期间必须执行更多工作,那么您应该在您的 WCF 客户端实现的服务合同接口上创建异步方法对。 执行此操作的最简单方法是使用 /async
ServiceModel 元数据实用工具(Svcutil.exe)上的选项。 有关示例,请参阅如何:以异步方式调用服务操作。
有关提高客户端性能的详细信息,请参阅 Middle-Tier 客户端应用程序。
使用户能够动态选择凭据
该 IInteractiveChannelInitializer 接口使应用程序能够显示一个用户界面,使用户能够在超时计时器启动之前选择用于创建通道的凭据。
应用程序开发人员可以通过两种方式使用插入的IInteractiveChannelInitializer。 在打开通道之前,客户端应用程序可以调用ClientBase<TChannel>.DisplayInitializationUI或IClientChannel.DisplayInitializationUI(或它们的异步版本)(显式方法),或调用第一个操作(隐式方法)。
如果使用隐式方法,应用程序必须调用ClientBase<TChannel>或IClientChannel扩展上的第一个操作。 如果它调用除第一个操作以外的任何操作,则会引发异常。
如果使用显式方法,应用程序必须按顺序执行以下步骤:
请调用ClientBase<TChannel>.DisplayInitializationUI或IClientChannel.DisplayInitializationUI(或其异步版本)。
初始化器返回后,调用Open方法,对IClientChannel对象或IClientChannel属性返回的ClientBase<TChannel>.InnerChannel对象进行操作。
调用操作。
建议生产质量应用程序通过采用显式方法来控制用户界面过程。
使用隐式方法的应用程序会调用用户界面初始化程序,但如果应用程序的用户无法在绑定的发送超时期内进行响应,则在用户界面返回时会抛出异常。