教程:使用 Azure WCF 中继向外部客户端公开本地 WCF REST 服务Tutorial: Expose an on-premises WCF REST service to external client by using Azure WCF Relay

本教程介绍如何使用 Azure 中继功能构建 WCF 中继客户端应用程序和服务。This tutorial describes how to build a WCF Relay client application and service using Azure Relay. 有关使用服务总线消息传送的类似教程,请参阅服务总线队列入门For a similar tutorial that uses Service Bus messaging, see Get started with Service Bus queues.

通过此教程,可以了解创建 WCF 中继客户端和服务应用程序的步骤。Working through this tutorial gives you an understanding of the steps to create a WCF Relay client and service application. 正如原始的 WCF,服务是公开一个或多个终结点的构造。Like their original WCF counterparts, a service is a construct that exposes one or more endpoints. 每个终结点公开一个或多个服务操作。Each endpoint exposes one or more service operations. 服务的终结点用于指定可在其中找到服务的地址、包含客户端必须与服务进行通信的信息的绑定,以及定义服务向其客户端提供的功能的协定。The endpoint of a service specifies an address where the service can be found, a binding that contains the information that a client must communicate with the service, and a contract that defines the functionality provided by the service to its clients. WCF 和 WCF 中继之间的主要区别在于:终结点在云中公开,而不是在本地计算机中公开。The main difference between WCF and WCF Relay is that the endpoint is exposed in the cloud instead of locally on your computer.

按顺序完成本教程的每个部分后,你将获得一个正常运行的服务。After you work through the sequence of sections in this tutorial, you'll have a running service. 此外,你还会获得一个可以调用服务操作的客户端。You'll also have a client that can invoke the operations of the service.

在本教程中,你将执行以下任务:You do the following tasks in this tutorial:

  • 安装本教程的必备组件。Install prerequisites for this tutorial.
  • 创建中继命名空间。Create a Relay namespace.
  • 创建 WCF 服务协定。Create a WCF service contract.
  • 实现 WCF 协定。Implement the WCF contract.
  • 托管并运行 WCF 服务以向中继服务注册。Host and run the WCF service to register with the Relay service.
  • 创建服务协定的 WCF 客户端。Create a WCF client for the service contract.
  • 配置 WCF 客户端。Configure the WCF client.
  • 实现 WCF 客户端。Implement the WCF client.
  • 运行应用程序。Run the applications.

先决条件Prerequisites

若要完成本教程,需要满足以下先决条件:To complete this tutorial, you need the following prerequisites:

创建中继命名空间Create a Relay namespace

第一步是创建命名空间并获取共享访问签名 (SAS) 密钥。The first step is to create a namespace, and to obtain a Shared Access Signature (SAS) key. 命名空间为每个通过中继服务公开的应用程序提供应用程序边界。A namespace provides an application boundary for each application exposed through the relay service. 创建服务命名空间时,系统会自动生成 SAS 密钥。An SAS key is automatically generated by the system when a service namespace is created. 服务命名空间与 SAS 密钥的组合为 Azure 提供了用于验证应用程序访问权限的凭据。The combination of service namespace and SAS key provides the credentials for Azure to authenticate access to an application.

  1. 登录 Azure 门户Sign in to the Azure portal.

  2. 选择“创建资源”。Select Create a resource. 然后,选择“集成” > “中继”。Then, select Integration > Relay. 如果未在列表中看到“中继”,请选择右上角的“查看全部”。If you don't see Relay in the list, select See All in the top-right corner.

  3. 选择“创建”,然后在“名称”字段中输入命名空间名称。Select Create, and enter a namespace name in the Name field. Azure 门户会检查该名称是否可用。Azure portal checks to see if the name is available.

  4. 选择要在其中创建命名空间的 Azure 订阅。Choose an Azure subscription in which to create the namespace.

  5. 对于资源组,选择一个要在其中放置命名空间的现有资源组,或创建一个新资源组。For Resource group, choose an existing resource group in which to place the namespace, or create a new one.

  6. 选择应托管该命名空间的国家或地区。Select the country or region in which your namespace should be hosted.

    创建命名空间

  7. 选择“创建” 。Select Create. Azure 门户将创建该命名空间并启用它。The Azure portal creates your namespace and enables it. 几分钟后,系统将为用户的帐户预配资源。After a few minutes, the system provisions resources for your account.

获取管理凭据Get management credentials

  1. 选择“所有资源”,然后选择新建的命名空间名称。Select All resources, and then choose the newly created namespace name.

  2. 选择“共享访问策略”。Select Shared access policies.

  3. 在“共享访问策略”下,选择“RootManageSharedAccessKey”。Under Shared access policies, select RootManageSharedAccessKey.

  4. 在“SAS 策略:RootManageSharedAccessKey”下,选择“主连接字符串”旁边的“复制”按钮。Under SAS Policy: RootManageSharedAccessKey, select the Copy button next to Primary Connection String. 此操作会将连接字符串复制到剪贴板,供以后使用。This action copies the connection string to your clipboard for later use. 将此值粘贴到记事本或其他某个临时位置。Paste this value into Notepad or some other temporary location.

  5. 重复上述步骤,将主密钥的值复制和粘贴到临时位置,供以后使用。Repeat the preceding step to copy and paste the value of Primary key to a temporary location for later use.

    connection-string

定义 WCF 服务协定Define a WCF service contract

服务协定指定服务支持的操作。The service contract specifies what operations the service supports. 操作是 Web 服务方法或函数。Operations are web service methods or functions. 约定通过定义 C++、C# 或 Visual Basic 接口来创建。Contracts are created by defining a C++, C#, or Visual Basic interface. 接口中的每个方法都对应一个特定的服务操作。Each method in the interface corresponds to a specific service operation. 必须将 ServiceContractAttribute 属性应用于每个接口,并且必须将 OperationContractAttribute 属性应用于每个操作。Each interface must have the ServiceContractAttribute attribute applied to it, and each operation must have the OperationContractAttribute attribute applied to it. 如果具有 ServiceContractAttribute 属性的接口中的方法没有 OperationContractAttribute 属性,则该方法是不公开的。If a method in an interface that has the ServiceContractAttribute attribute doesn't have the OperationContractAttribute attribute, that method isn't exposed. 该过程后面的示例中提供了这些任务的代码。The code for these tasks is provided in the example following the procedure. 有关协定和服务的更多讨论,请参阅设计和实现服务For a larger discussion of contracts and services, see Designing and Implementing Services.

使用接口创建中继协定Create a relay contract with an interface

  1. 以管理员身份启动 Microsoft Visual Studio。Start Microsoft Visual Studio as an administrator. 为此,请右键单击 Visual Studio 程序图标,并选择“以管理员身份运行”。To do so, right-click the Visual Studio program icon, and select Run as administrator.

  2. 在 Visual Studio 中,选择“新建项目”。In Visual Studio, select Create a new project.

  3. 在“创建新项目”中,选择适用于 C# 的“控制台应用(.NET Framework)”,然后选择“下一步”。 In Create a new project, choose Console App (.NET Framework) for C# and select Next.

  4. 将项目命名为 EchoService 并选择“创建”。Name the project EchoService and select Create.

    创建控制台应用

  5. 在“解决方案资源管理器”中,右键单击项目,并选择“管理 NuGet 包”。In Solution Explorer, right-click the project and select Manage NuGet Packages. 在“NuGet 包管理器”中选择“浏览”,然后搜索并选择“WindowsAzure.ServiceBus”。 In the NuGet Package Manager, select Browse, then search for and choose WindowsAzure.ServiceBus. 选择“安装”并接受使用条款。Select Install, and accept the terms of use.

    服务总线包

    该包自动添加对服务总线库和 WCF System.ServiceModel 的引用。This package automatically adds references to the Service Bus libraries and the WCF System.ServiceModel. System.ServiceModel 是用于以编程方式访问 WCF 基本功能的命名空间。System.ServiceModel is the namespace that enables you to programmatically access the basic features of WCF. 服务总线使用 WCF 的许多对象和属性来定义服务约定。Service Bus uses many of the objects and attributes of WCF to define service contracts.

  6. Program.cs 顶部添加以下 using 语句:Add the following using statements at the top of Program.cs:

    using System.ServiceModel;
    using Microsoft.ServiceBus;
    
  7. 将命名空间名称从其默认名称 EchoService 更改为 Microsoft.ServiceBus.SamplesChange the namespace name from its default name of EchoService to Microsoft.ServiceBus.Samples.

    重要

    本教程使用 C# 命名空间 Microsoft.ServiceBus.Samples,它是基于协定的管理类型的命名空间,此类型用于配置 WCF 客户端部分中的配置文件。This tutorial uses the C# namespace Microsoft.ServiceBus.Samples which is the namespace of the contract-based managed type that is used in the configuration file in the Configure the WCF client section. 在生成此示例时,可以指定所需的任何命名空间。You can specify any namespace you want when you build this sample. 但是,只有在应用程序配置文件中相应地修改协定和服务的命名空间之后,本教程才会生效。However, the tutorial will not work unless you then modify the namespaces of the contract and service accordingly, in the application configuration file. App.config 文件中指定的命名空间必须与在 C# 文件中指定的命名空间相同。The namespace specified in the App.config file must be the same as the namespace specified in your C# files.

  8. 直接在 Microsoft.ServiceBus.Samples 命名空间声明后面(但在命名空间内)定义一个名为 IEchoContract 的新接口,然后将 ServiceContractAttribute 属性应用于该接口,命名空间值为 https://samples.microsoft.com/ServiceModel/Relay/Directly after the Microsoft.ServiceBus.Samples namespace declaration, but within the namespace, define a new interface named IEchoContract and apply the ServiceContractAttribute attribute to the interface with a namespace value of https://samples.microsoft.com/ServiceModel/Relay/. 在命名空间声明的后面粘贴以下代码:Paste the following code after the namespace declaration:

    [ServiceContract(Name = "IEchoContract", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
    public interface IEchoContract
    {
    }
    

    该命名空间值不同于在整个代码范围内使用的命名空间。The namespace value differs from the namespace that you use throughout the scope of your code. 相反,该命名空间值用作此协定的唯一标识符。Instead, the namespace value is used as a unique identifier for this contract. 显式指定命名空间可防止将默认的命名空间值添加到约定名称中。Specifying the namespace explicitly prevents the default namespace value from being added to the contract name.

    备注

    通常情况下,服务协定命名空间包含一个包括版本信息的命名方案。Typically, the service contract namespace contains a naming scheme that includes version information. 服务协定命名空间中包括的版本信息可以使服务通过将新服务协定定义为新命名空间并将其公开到新的终结点上,来隔离重大更改。Including version information in the service contract namespace enables services to isolate major changes by defining a new service contract with a new namespace and exposing it on a new endpoint. 以这种方式,客户端可以继续使用旧的服务协定,而无需进行更新。In this manner, clients can continue to use the old service contract without having to be updated. 版本信息可能包含日期或内部版本号。Version information can consist of a date or a build number. 有关详细信息,请参阅 服务版本控制For more information, see Service Versioning. 对于本教程,服务协定命名空间的命名方案不包含版本信息。For this tutorial, the naming scheme of the service contract namespace does not contain version information.

  9. IEchoContract 接口中,为 IEchoContract 协定在接口中公开的单个操作声明一个方法,然后将 OperationContractAttribute 属性应用到你希望将其作为公共 WCF 中继协定的一部分进行公开的方法中,如下所示:Within the IEchoContract interface, declare a method for the single operation the IEchoContract contract exposes in the interface and apply the OperationContractAttribute attribute to the method that you want to expose as part of the public WCF Relay contract, as follows:

    [OperationContract]
    string Echo(string text);
    
  10. 直接在 IEchoContract 接口定义之后声明从 IEchoContract 中继承并同样继承到 IClientChannel 接口的通道,如下所示:Directly after the IEchoContract interface definition, declare a channel that inherits from both IEchoContract and also to the IClientChannel interface, as shown here:

    public interface IEchoChannel : IEchoContract, IClientChannel { }
    

    通道是主机和客户端用来互相传递信息的 WCF 对象。A channel is the WCF object through which the host and client pass information to each other. 随后,将针对通道编写代码,以在两个应用程序之间回显信息。Later, you'll write code against the channel to echo information between the two applications.

  11. 选择“生成” > “生成解决方案”,或按 Ctrl+Shift+B 以确认到目前为止操作的准确性 。Select Build > Build Solution or select Ctrl+Shift+B to confirm the accuracy of your work so far.

WCF 协定的示例Example of a WCF contract

以下代码显示了定义 WCF 中继协定的基本接口。The following code shows a basic interface that defines a WCF Relay contract.

using System;
using System.ServiceModel;

namespace Microsoft.ServiceBus.Samples
{
    [ServiceContract(Name = "IEchoContract", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
    public interface IEchoContract
    {
        [OperationContract]
        String Echo(string text);
    }

    public interface IEchoChannel : IEchoContract, IClientChannel { }

    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

既然已创建接口,可以实现该接口。Now that the interface is created, you can implement the interface.

实现 WCF 协定Implement the WCF contract

创建 Azure 中继首先需要使用接口创建协定。Creating an Azure relay requires that you first create the contract by using an interface. 有关创建接口的详细信息,请参阅上一部分。For more information about creating the interface, see the previous section. 下一个过程实现接口。The next procedure implements the interface. 此任务包括创建名为 EchoService 的类,用于实现用户定义的 IEchoContract 接口。This task involves creating a class named EchoService that implements the user-defined IEchoContract interface. 实现接口后,即可使用 App.config 配置文件配置接口。After you implement the interface, you then configure the interface using an App.config configuration file. 配置文件包含应用程序所需的信息。The configuration file contains necessary information for the application. 此信息包括服务的名称、协定的名称,以及用来与中继服务通信的协议类型。This information includes the name of the service, the name of the contract, and the type of protocol that is used to communicate with the relay service. 该过程后面的示例中提供了这些任务所用的代码。The code used for these tasks is provided in the example that follows the procedure. 有关如何实现服务协定的更多常规讨论,请参阅实现服务协定For a more general discussion about how to implement a service contract, see Implementing Service Contracts.

  1. 紧随 IEchoContract 接口定义之后,创建名为 EchoService 的新类。Create a new class named EchoService directly after the definition of the IEchoContract interface. EchoService 类实现 IEchoContract 接口。The EchoService class implements the IEchoContract interface.

    class EchoService : IEchoContract
    {
    }
    

    与其他接口实现类似,可以在另一个文件中实现定义。Similar to other interface implementations, you can implement the definition in a different file. 但是,在本教程中,实现所在的文件与接口定义和 Main() 方法所在的文件相同。However, for this tutorial, the implementation is located in the same file as the interface definition and the Main() method.

  2. ServiceBehaviorAttribute 属性应用于 IEchoContract 接口。Apply the ServiceBehaviorAttribute attribute to the IEchoContract interface. 该属性指定服务名称和命名空间。The attribute specifies the service name and namespace. 完成后, EchoService 类将如下所示:After doing so, the EchoService class appears as follows:

    [ServiceBehavior(Name = "EchoService", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
    class EchoService : IEchoContract
    {
    }
    
  3. EchoService 类中,实现 IEchoContract 接口中定义的 Echo 方法。Implement the Echo method defined in the IEchoContract interface in the EchoService class.

    public string Echo(string text)
    {
        Console.WriteLine("Echoing: {0}", text);
        return text;
    }
    
  4. 选择“生成” > “生成解决方案”,或按 Ctrl+Shift+B 。Select Build > Build Solution or select Ctrl+Shift+B.

定义服务主机的配置Define the configuration for the service host

该配置文件类似于 WCF 配置文件。The configuration file is similar to a WCF configuration file. 其中包括服务名称、终结点和绑定。It includes the service name, endpoint, and binding. 终结点是 Azure 中继公开的、让客户端和主机相互通信的位置。The endpoint is the location Azure Relay exposes for clients and hosts to communicate with each other. 绑定是用于通信的协议类型。The binding is the type of protocol that is used to communicate. 此处的主要差别在于,配置的服务终结点是指 NetTcpRelayBinding 绑定,它不是 .NET Framework 的一部分。The main difference is that this configured service endpoint refers to a NetTcpRelayBinding binding, which isn't part of the .NET Framework. NetTcpRelayBinding 是由服务定义的绑定之一。NetTcpRelayBinding is one of the bindings defined by the service.

  1. 在“解决方案资源管理器”中,双击“App.config”在 Visual Studio 编辑器中将其打开。In Solution Explorer, double-click App.config to open the file in the Visual Studio editor.

  2. <appSettings> 元素中,将占位符替换为服务命名空间的名称以及在先前步骤中复制的 SAS 密钥。In the <appSettings> element, replace the placeholders with the name of your service namespace, and the SAS key that you copied in an earlier step.

  3. <system.serviceModel> 标记中,添加 <services> 元素。Within the <system.serviceModel> tags, add a <services> element. 可以在单个配置文件中定义多个中继应用程序。You can define multiple relay applications in a single configuration file. 但是,本教程只定义一个。However, this tutorial defines only one.

    <?xmlversion="1.0"encoding="utf-8"?>
    <configuration>
      <system.serviceModel>
        <services>
    
        </services>
      </system.serviceModel>
    </configuration>
    
  4. <services> 元素中,添加 <service> 元素来定义服务名称。Within the <services> element, add a <service> element to define the name of the service.

    <service name="Microsoft.ServiceBus.Samples.EchoService">
    </service>
    
  5. <service> 元素中,定义终结点协定的位置,以及终结点绑定的类型。Within the <service> element, define the location of the endpoint contract, and also the type of binding for the endpoint.

    <endpoint contract="Microsoft.ServiceBus.Samples.IEchoContract" binding="netTcpRelayBinding"/>
    

    终结点用于定义客户端会在何处查找主机应用程序。The endpoint defines where the client will look for the host application. 接下来,本教程使用此步骤创建一个通过 Azure 中继完全公开主机的 URI。Later, the tutorial uses this step to create a URI that fully exposes the host through Azure Relay. 绑定声明我们将 TCP 用作协议,以与中继服务进行通信。The binding declares that we're using TCP as the protocol to communicate with the relay service.

  6. 选择“生成” > “生成解决方案”,或按 Ctrl+Shift+B 以确认到目前为止操作的准确性 。Select Build > Build Solution or select Ctrl+Shift+B to confirm the accuracy of your work so far.

服务协定的实现示例Example of implementation of a service contract

下面的代码显示服务协定的实现。The following code shows the implementation of the service contract.

[ServiceBehavior(Name = "EchoService", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]

    class EchoService : IEchoContract
    {
        public string Echo(string text)
        {
            Console.WriteLine("Echoing: {0}", text);
            return text;
        }
    }

以下代码显示了与该服务主机关联的 App.config 文件的基本格式。The following code shows the basic format of the App.config file associated with the service host.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Microsoft.ServiceBus.Samples.EchoService">
        <endpoint contract="Microsoft.ServiceBus.Samples.IEchoContract" binding="netTcpRelayBinding" />
      </service>
    </services>
    <extensions>
      <bindingExtensions>
        <add name="netTcpRelayBinding"
                    type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </bindingExtensions>
    </extensions>
  </system.serviceModel>
</configuration>

托管并运行 WCF 服务以向中继服务注册Host and run the WCF service to register with the relay service

此步骤介绍如何运行 Azure 中继服务。This step describes how to run an Azure Relay service.

创建中继凭据Create the relay credentials

  1. Main()中,创建两个变量,将命名空间和从控制台窗口中读取的 SAS 密钥存储在其中。In Main(), create two variables in which to store the namespace and the SAS key that are read from the console window.

    Console.Write("Your Service Namespace: ");
    string serviceNamespace = Console.ReadLine();
    Console.Write("Your SAS key: ");
    string sasKey = Console.ReadLine();
    

    随后将使用 SAS 密钥来访问你的项目。The SAS key will be used later to access your project. 命名空间作为参数传递给 CreateServiceUri 以创建服务 URI。The namespace is passed as a parameter to CreateServiceUri to create a service URI.

  2. 使用 TransportClientEndpointBehavior 对象声明使用 SAS 密钥作为凭据类型。Using a TransportClientEndpointBehavior object, declare that you'll be using an SAS key as the credential type. 在最后一步中添加的代码后直接添加以下代码。Add the following code directly after the code added in the last step.

    TransportClientEndpointBehavior sasCredential = new TransportClientEndpointBehavior();
    sasCredential.TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("RootManageSharedAccessKey", sasKey);
    

为服务创建基本地址Create a base address for the service

在上一部分添加的代码后面,为服务的基址创建 Uri 实例。After the code you added in the previous section, create a Uri instance for the base address of the service. 此 URI 指定服务总线方案、命名空间,以及服务接口的路径。This URI specifies the Service Bus scheme, the namespace, and the path of the service interface.

Uri address = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace, "EchoService");

值“sb”是服务总线方案的缩写。The value "sb" is an abbreviation for the Service Bus scheme. 它指示我们正在使用 TCP 作为协议。It indicates that we're using TCP as the protocol. 先前当 NetTcpRelayBinding 被指定为绑定时,在配置文件中也指示了此方案。This scheme was also previously indicated in the configuration file, when NetTcpRelayBinding was specified as the binding.

对于本教程中,URI 是 sb://putServiceNamespaceHere.chinacloudapi.cn/EchoServiceFor this tutorial, the URI is sb://putServiceNamespaceHere.chinacloudapi.cn/EchoService.

创建并配置服务主机Create and configure the service host

  1. 仍在 Main() 中,将连接模式设置为 AutoDetectStill working in Main(), set the connectivity mode to AutoDetect.

    ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.AutoDetect;
    

    连接模式描述服务用于与中继服务进行通信的协议;连接模式为 HTTP 或 TCP。The connectivity mode describes the protocol the service uses to communicate with the relay service; either HTTP or TCP. 使用默认设置 AutoDetect,服务尝试通过 TCP(如果可用)或 HTTP(如果 TCP 不可用)连接到 Azure 中继。Using the default setting AutoDetect, the service attempts to connect to Azure Relay over TCP if it's available, and HTTP if TCP isn't available. 结果与服务为客户端通信指定的协议不同。This result differs from the protocol the service specifies for client communication. 为客户端通信指定的协议由所使用的绑定所决定。That protocol is determined by the binding used. 例如,服务可以使用 BasicHttpRelayBinding 绑定,该绑定指定其终结点通过 HTTP 与客户端通信。For example, a service can use the BasicHttpRelayBinding binding, which specifies that its endpoint communicates with clients over HTTP. 同一个服务可以指定 ConnectivityMode.AutoDetect,以便服务通过 TCP 与 Azure 中继通信。That same service could specify ConnectivityMode.AutoDetect so that the service communicates with Azure Relay over TCP.

  2. 使用之前在本部分中创建的 URI 创建服务主机。Create the service host, using the URI created earlier in this section.

    ServiceHost host = new ServiceHost(typeof(EchoService), address);
    

    该服务主机是可实例化服务的 WCF 对象。The service host is the WCF object that instantiates the service. 在这里传递想要创建的服务类型(EchoService 类型),以及想要公开服务的地址。Here, you pass it the type of service you want to create, an EchoService type, and also to the address at which you want to expose the service.

  3. Program.cs 文件的顶部,添加对 System.ServiceModel.DescriptionMicrosoft.ServiceBus.Description 的引用。At the top of the Program.cs file, add references to System.ServiceModel.Description and Microsoft.ServiceBus.Description.

    using System.ServiceModel.Description;
    using Microsoft.ServiceBus.Description;
    
  4. 返回到 Main(),配置终结点以启用公开访问。Back in Main(), configure the endpoint to enable public access.

    IEndpointBehavior serviceRegistrySettings = new ServiceRegistrySettings(DiscoveryType.Public);
    

    此步骤告知中继服务可以通过检查项目的 Atom 馈送公开找到应用程序。This step informs the relay service that your application can be found publicly by examining the Atom feed for your project. 如果将 DiscoveryType 设置为 private,客户端仍可以访问该服务。If you set DiscoveryType to private, a client could still access the service. 但是,当搜索 Relay 命名空间时不会显示该服务。However, the service wouldn't appear when it searches the Relay namespace. 相反,客户端必须事先知道终结点路径。Instead, the client would have to know the endpoint path beforehand.

  5. 将服务凭据应用到 App.config 文件中定义的服务终结点:Apply the service credentials to the service endpoints defined in the App.config file:

    foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
    {
        endpoint.Behaviors.Add(serviceRegistrySettings);
        endpoint.Behaviors.Add(sasCredential);
    }
    

    如前所述,可能已经在配置文件中声明多个服务和终结点。As stated previously, you could have declared multiple services and endpoints in the configuration file. 如果已配置,此代码将遍历配置文件并且搜索可能应用了凭据的每个终结点。If you had, this code would traverse the configuration file and search for every endpoint to which it should apply your credentials. 对于本教程,配置文件只有一个终结点。For this tutorial, the configuration file has only one endpoint.

打开服务主机Open the service host

  1. 仍在 Main() 中,添加以下行以打开服务。Still in Main(), add the following line to open the service.

    host.Open();
    
  2. 通知用户该服务正在运行,并说明如何关闭服务。Inform the user that the service is running, and explain how to shut down the service.

    Console.WriteLine("Service address: " + address);
    Console.WriteLine("Press [Enter] to exit");
    Console.ReadLine();
    
  3. 完成后,关闭服务主机。When finished, close the service host.

    host.Close();
    
  4. 按 Ctrl+Shift+B 生成项目。Select Ctrl+Shift+B to build the project.

在控制台应用程序中托管服务的示例Example that hosts a service in a console application

完成的服务代码应如下所示。Your completed service code should appear as follows. 该代码包括本教程中前面步骤中使用的服务协定和实现,并将服务托管在控制台应用程序中。The code includes the service contract and implementation from previous steps in the tutorial, and hosts the service in a console application.

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using Microsoft.ServiceBus;
using Microsoft.ServiceBus.Description;

namespace Microsoft.ServiceBus.Samples
{
    [ServiceContract(Name = "IEchoContract", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
    public interface IEchoContract
    {
        [OperationContract]
        String Echo(string text);
    }

    public interface IEchoChannel : IEchoContract, IClientChannel { };

    [ServiceBehavior(Name = "EchoService", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
    class EchoService : IEchoContract
    {
        public string Echo(string text)
        {
            Console.WriteLine("Echoing: {0}", text);
            return text;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {

            ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.AutoDetect;         

            Console.Write("Your Service Namespace: ");
            string serviceNamespace = Console.ReadLine();
            Console.Write("Your SAS key: ");
            string sasKey = Console.ReadLine();

           // Create the credentials object for the endpoint.
            TransportClientEndpointBehavior sasCredential = new TransportClientEndpointBehavior();
            sasCredential.TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("RootManageSharedAccessKey", sasKey);

            // Create the service URI based on the service namespace.
            Uri address = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace, "EchoService");

            // Create the service host reading the configuration.
            ServiceHost host = new ServiceHost(typeof(EchoService), address);

            // Create the ServiceRegistrySettings behavior for the endpoint.
            IEndpointBehavior serviceRegistrySettings = new ServiceRegistrySettings(DiscoveryType.Public);

            // Add the Relay credentials to all endpoints specified in configuration.
            foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
            {
                endpoint.Behaviors.Add(serviceRegistrySettings);
                endpoint.Behaviors.Add(sasCredential);
            }

            // Open the service.
            host.Open();

            Console.WriteLine("Service address: " + address);
            Console.WriteLine("Press [Enter] to exit");
            Console.ReadLine();

            // Close the service.
            host.Close();
        }
    }
}

创建服务协定的 WCF 客户端Create a WCF client for the service contract

下一任务是创建客户端应用程序,并定义稍后要实现的服务协定。The next task is to create a client application and define the service contract you'll implement later. 这些步骤类似于创建服务的步骤:定义协定、编辑 App.config 文件、使用凭据连接到中继服务等。These steps resemble the steps used to create a service: defining a contract, editing an App.config file, using credentials to connect to the relay service, and so on. 该过程后面的示例中提供了这些任务所用的代码。The code used for these tasks is provided in the example following the procedure.

  1. 在当前 Visual Studio 解决方案中为客户端创建一个新的项目:Create a new project in the current Visual Studio solution for the client:

    1. 在“解决方案资源管理器”中,右键单击当前解决方案(而非项目),并选择“添加” > “新建项目”。In Solution Explorer, right-click the current solution (not the project), and select Add > New Project.
    2. 在“添加新项目”中,选择适用于 C# 的“控制台应用(.NET Framework)”,然后选择“下一步”。 In Add a new project, select Console App (.NET Framework) for C#, and select Next.
    3. 将项目命名为 EchoClient,然后选择“创建”。Name project EchoClient and select Create.
  2. 在“解决方案资源管理器”中的“EchoClient”项目内,双击“Program.cs”文件以在编辑器中将其打开(如果尚未打开)。 In Solution Explorer, in the EchoClient project, double-click Program.cs to open the file in the editor, if it isn't already open.

  3. 将命名空间名称从其默认名称 EchoClient 更改为 Microsoft.ServiceBus.SamplesChange the namespace name from its default name of EchoClient to Microsoft.ServiceBus.Samples.

  4. 安装服务总线 NuGet 包Install the Service Bus NuGet package:

    1. 在“解决方案资源管理器”中,右键单击“EchoClient”并选择“管理 NuGet 包” 。In Solution Explorer, right-click EchoClient and then select Manage NuGet Packages.

    2. 选择“浏览”,然后搜索并选择“WindowsAzure.ServiceBus”。 Select Browse, then search for and select WindowsAzure.ServiceBus. 选择“安装”并接受使用条款。Select Install, and accept the terms of use.

      安装服务总线包

  5. Program.cs 文件中的 System.ServiceModel 命名空间添加 using 语句。Add a using statement for the System.ServiceModel namespace in the Program.cs file.

    using System.ServiceModel;
    
  6. 如下面的示例中所示,将服务协定定义添加到命名空间。Add the service contract definition to the namespace, as shown in the following example. 此定义等同于 Service 项目中所使用的定义。This definition is identical to the definition used in the Service project. Microsoft.ServiceBus.Samples 命名空间的顶部添加此代码。Add this code at the top of the Microsoft.ServiceBus.Samples namespace.

    [ServiceContract(Name = "IEchoContract", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
    public interface IEchoContract
    {
        [OperationContract]
        string Echo(string text);
    }
    
    public interface IEchoChannel : IEchoContract, IClientChannel { }
    
  7. 按 Ctrl+Shift+B 生成客户端。Select Ctrl+Shift+B to build the client.

EchoClient 项目的示例Example of the EchoClient project

以下代码演示了 EchoClient 项目中的 Program.cs 文件的当前状态。The following code shows the current status of the Program.cs file in the EchoClient project.

using System;
using Microsoft.ServiceBus;
using System.ServiceModel;

namespace Microsoft.ServiceBus.Samples
{

    [ServiceContract(Name = "IEchoContract", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
    public interface IEchoContract
    {
        [OperationContract]
        string Echo(string text);
    }

    public interface IEchoChannel : IEchoContract, IClientChannel { }

    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

配置 WCF 客户端Configure the WCF client

在此步骤中,可以为之前在本教程中创建的访问服务的基本客户端应用程序创建 App.config 文件。In this step, you create an App.config file for a basic client application that accesses the service created previously in this tutorial. App.config 文件用于定义终结点的协定、绑定和名称。This App.config file defines the contract, binding, and name of the endpoint. 该过程后面的示例中提供了这些任务所用的代码。The code used for these tasks is provided in the example following the procedure.

  1. 在“解决方案资源管理器”中的“EchoClient”项目内,双击“App.config”以在 Visual Studio 编辑器中打开该文件 。In Solution Explorer, in the EchoClient project, double-click App.config to open the file in the Visual Studio editor.

  2. <appSettings> 元素中,将占位符替换为服务命名空间的名称以及在先前步骤中复制的 SAS 密钥。In the <appSettings> element, replace the placeholders with the name of your service namespace, and the SAS key that you copied in an earlier step.

  3. system.serviceModel 元素中添加 <client> 元素。Within the system.serviceModel element, add a <client> element.

    <?xmlversion="1.0"encoding="utf-8"?>
    <configuration>
      <system.serviceModel>
        <client>
        </client>
      </system.serviceModel>
    </configuration>
    

    此代码声明要定义 WCF 样式的客户端应用程序。This code declares that you're defining a WCF-style client application.

  4. client 元素中,定义终结点的名称、协定和绑定类型。Within the client element, define the name, contract, and binding type for the endpoint.

    <endpoint name="RelayEndpoint"
                    contract="Microsoft.ServiceBus.Samples.IEchoContract"
                    binding="netTcpRelayBinding"/>
    

    此代码定义终结点的名称。This code defines the name of the endpoint. 它还定义服务中定义的协定,以及客户端应用程序使用 TCP 与 Azure 中继进行通信的事实。It also defines the contract defined in the service and the fact that the client application uses TCP to communicate with Azure Relay. 终结点名称在下一步中用于将此终结点配置与服务 URI 链接。The endpoint name is used in the next step to link this endpoint configuration with the service URI.

  5. 选择“文件” > “全部保存”。 Select File > Save All.

App.config 文件的示例Example of the App.config file

以下代码演示 Echo 客户端的 App.config 文件。The following code shows the App.config file for the Echo client.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <client>
      <endpoint name="RelayEndpoint"
                      contract="Microsoft.ServiceBus.Samples.IEchoContract"
                      binding="netTcpRelayBinding"/>
    </client>
    <extensions>
      <bindingExtensions>
        <add name="netTcpRelayBinding"
                    type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </bindingExtensions>
    </extensions>
  </system.serviceModel>
</configuration>

实现 WCF 客户端Implement the WCF client

在此部分,实现可访问之前在本教程中创建的服务的基本客户端应用程序。In this section, you implement a basic client application that accesses the service you created previously in this tutorial. 与服务相似,该客户端执行许多相同的操作访问 Azure 中继:Similar to the service, the client does many of the same operations to access Azure Relay:

  • 设置连接模式。Sets the connectivity mode.
  • 创建用于定位主机服务的 URI。Creates the URI that locates the host service.
  • 定义安全凭据。Defines the security credentials.
  • 将凭据应用到连接。Applies the credentials to the connection.
  • 打开连接。Opens the connection.
  • 执行应用程序特定的任务。Performs the application-specific tasks.
  • 关闭连接。Closes the connection.

但是,主要的区别之一在于,客户端应用程序使用通道连接到中继服务。However, one of the main differences is that the client application uses a channel to connect to the relay service. 服务使用对 ServiceHost 的调用。The service uses a call to ServiceHost. 该过程后面的示例中提供了这些任务所用的代码。The code used for these tasks is provided in the example following the procedure.

实现客户端应用程序Implement a client application

  1. 将连接模式设置为 AutoDetectSet the connectivity mode to AutoDetect. EchoClient 应用程序的 Main() 方法中添加以下代码。Add the following code inside the Main() method of the EchoClient application.

    ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.AutoDetect;
    
  2. 定义变量以保存用于服务命名空间的值,以及从控制台读取的 SAS 密钥。Define variables to hold the values for the service namespace, and SAS key that are read from the console.

    Console.Write("Your Service Namespace: ");
    string serviceNamespace = Console.ReadLine();
    Console.Write("Your SAS Key: ");
    string sasKey = Console.ReadLine();
    
  3. 创建用于定义中继项目中主机位置的 URI。Create the URI that defines the location of the host in your Relay project.

    Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace, "EchoService");
    
  4. 创建服务命名空间终结点的凭据对象。Create the credential object for your service namespace endpoint.

    TransportClientEndpointBehavior sasCredential = new TransportClientEndpointBehavior();
    sasCredential.TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("RootManageSharedAccessKey", sasKey);
    
  5. 创建加载在 App.config 文件中所述的配置的通道工厂。Create the channel factory that loads the configuration described in the App.config file.

    ChannelFactory<IEchoChannel> channelFactory = new ChannelFactory<IEchoChannel>("RelayEndpoint", new EndpointAddress(serviceUri));
    

    通道工厂是创建通道(通过该通道,服务和客户端可以进行通信)的一个 WCF 对象。A channel factory is a WCF object that creates a channel through which the service and client applications communicate.

  6. 应用凭据。Apply the credentials.

    channelFactory.Endpoint.Behaviors.Add(sasCredential);
    
  7. 创建并打开服务通道。Create and open the channel to the service.

    IEchoChannel channel = channelFactory.CreateChannel();
    channel.Open();
    
  8. 编写用于回显的基本用户界面和功能。Write the basic user interface and functionality for the echo.

    Console.WriteLine("Enter text to echo (or [Enter] to exit):");
    string input = Console.ReadLine();
    while (input != String.Empty)
    {
        try
        {
            Console.WriteLine("Server echoed: {0}", channel.Echo(input));
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: " + e.Message);
        }
        input = Console.ReadLine();
    }
    

    代码使用通道对象的实例作为服务代理。The code uses the instance of the channel object as a proxy for the service.

  9. 关闭通道,并关闭工厂。Close the channel, and close the factory.

    channel.Close();
    channelFactory.Close();
    

本教程的示例代码Example code for this tutorial

完成的代码应如下所示。Your completed code should appear as follows. 此代码演示如何创建客户端应用程序、如何调用服务操作以及如何在完成操作调用后关闭客户端。This code shows how to create a client application, how to call the operations of the service, and how to close the client after the operation call is finished.

using System;
using Microsoft.ServiceBus;
using System.ServiceModel;

namespace Microsoft.ServiceBus.Samples
{
    [ServiceContract(Name = "IEchoContract", Namespace = "https://samples.microsoft.com/ServiceModel/Relay/")]
    public interface IEchoContract
    {
        [OperationContract]
        String Echo(string text);
    }

    public interface IEchoChannel : IEchoContract, IClientChannel { }

    class Program
    {
        static void Main(string[] args)
        {
            ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.AutoDetect;

            Console.Write("Your Service Namespace: ");
            string serviceNamespace = Console.ReadLine();
            Console.Write("Your SAS Key: ");
            string sasKey = Console.ReadLine();

            Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace, "EchoService");

            TransportClientEndpointBehavior sasCredential = new TransportClientEndpointBehavior();
            sasCredential.TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("RootManageSharedAccessKey", sasKey);

            ChannelFactory<IEchoChannel> channelFactory = new ChannelFactory<IEchoChannel>("RelayEndpoint", new EndpointAddress(serviceUri));

            channelFactory.Endpoint.Behaviors.Add(sasCredential);

            IEchoChannel channel = channelFactory.CreateChannel();
            channel.Open();

            Console.WriteLine("Enter text to echo (or [Enter] to exit):");
            string input = Console.ReadLine();
            while (input != String.Empty)
            {
                try
                {
                    Console.WriteLine("Server echoed: {0}", channel.Echo(input));
                }
                catch (Exception e)
                {
                    Console.WriteLine("Error: " + e.Message);
                }
                input = Console.ReadLine();
            }

            channel.Close();
            channelFactory.Close();

        }
    }
}

运行应用程序Run the applications

  1. 按 Ctrl+Shift+B 生成解决方案。Select Ctrl+Shift+B to build the solution. 此操作生成在先前步骤中创建的客户端项目和服务项目。This action builds both the client project and the service project that you created in the previous steps.

  2. 在运行客户端应用程序之前,必须确保服务应用程序正在运行。Before running the client application, you must make sure that the service application is running. 在“解决方案资源管理器”中,右键单击“EchoService”解决方案并选择“属性” 。In Solution Explorer, right-click the EchoService solution, then select Properties.

  3. 在“属性页”中选择“通用属性” > “启动项目”,然后选择“多个启动项目” 。In Property Pages, Common Properties > Startup Project, then choose Multiple startup projects. EchoService 显示在列表的最前面。Make sure EchoService appears first in the list.

  4. EchoServiceEchoClient 项目的“操作”框设置为“启动”。Set the Action box for both the EchoService and EchoClient projects to Start.

    项目属性页

  5. 选择“项目依赖项”。Select Project Dependencies. 在“项目”中选择“EchoClient”。 In Projects, select EchoClient. 对于“依赖于”,请确保选择“EchoService”。 For Depends on, make sure EchoService is selected.

    项目依赖项

  6. 选择“确定”关闭“属性页”。 Select OK to close Property Pages.

  7. 按 F5 运行这两个项目。Select F5 to run both projects.

  8. 此时会打开两个控制台窗口并提示输入命名空间名称。Both console windows open and prompt you for the namespace name. 必须先运行服务,因此请在“EchoService”控制台窗口中输入命名空间,然后按 Enter。The service must run first, so in the EchoService console window, enter the namespace and then select Enter.

  9. 接下来,控制台会提示输入 SAS 密钥。Next, the console prompts you for your SAS key. 输入 SAS 密钥并按 Enter。Enter the SAS key and select Enter.

    以下是来自控制台窗口的示例输出。Here is example output from the console window. 此处的值只是示例。The values here are just examples.

    Your Service Namespace: myNamespace

    Your SAS Key: <SAS key value>

    服务应用程序将其正在侦听的地址打印到控制台窗口中,如下面的示例中所示。The service application prints to the console window the address on which it's listening, as seen in the following example.

    Service address: sb://mynamespace.servicebus.chinacloudapi.cn/EchoService/

    Press [Enter] to exit

  10. EchoClient 控制台窗口中,输入之前为服务应用程序输入的相同信息。In the EchoClient console window, enter the same information that you entered previously for the service application. 为客户端应用程序输入相同的服务命名空间和 SAS 密钥值。Enter the same service namespace and SAS key values for the client application.

  11. 输入这些值后,客户端将打开服务通道并提示你输入如以下控制台输出示例中所示的某些文本。After entering these values, the client opens a channel to the service and prompts you to enter some text as seen in the following console output example.

    Enter text to echo (or [Enter] to exit):

    输入要发送到服务应用程序的一些文本,然后按 Enter。Enter some text to send to the service application and select Enter. 此文本通过 Echo 服务操作发送到服务并显示在服务控制台窗口中,如下面的示例输出所示。This text is sent to the service through the Echo service operation and appears in the service console window as in the following example output.

    Echoing: My sample text

    客户端应用程序接收 Echo 操作的返回值(此为原始文本),并将其打印到控制台窗口。The client application receives the return value of the Echo operation, which is the original text, and prints it to its console window. 以下文本是来自客户端控制台窗口的示例输出。The following text is example output from the client console window.

    Server echoed: My sample text

  12. 可以继续以这种方式将来自客户端的短信发送至服务。You can continue sending text messages from the client to the service in this manner. 完成后,在客户端和服务控制台窗口中按 Enter 以结束这两个应用程序。When you're finished, select Enter in the client and service console windows to end both applications.

后续步骤Next steps

转到以下教程:Advance to the following tutorial: