如何将 Azure Relay WCF 中继与 .NET 配合使用How to use Azure Relay WCF relays with .NET

本文介绍如何使用 Azure 中继服务。This article describes how to use the Azure Relay service. 相关示例用 C# 编写并使用服务总线程序集中包含的 Windows Communication Foundation (WCF) API 及扩展。The samples are written in C# and use the Windows Communication Foundation (WCF) API with extensions contained in the Service Bus assembly. 有关 Azure 中继的详细信息,请参阅 Azure 中继概述For more information about Azure relay, see the Azure Relay overview.

Note

若要完成本教程,需要一个 Azure 帐户。To complete this tutorial, you need an Azure account. 可以注册免费试用版You can sign up for a free trial.

什么是 WCF 中继?What is WCF Relay?

通过 Azure WCF 中继服务可构建在 Azure 数据中心和自己的本地企业环境中运行的混合应用程序。The Azure WCF Relay service enables you to build hybrid applications that run in both an Azure datacenter and your own on-premises enterprise environment. 中继服务可简化这一过程,它允许安全地向公有云公开位于企业网络内的 Windows Communication Foundation (WCF) 服务,而无需打开防火墙连接,也无需对企业网络基础结构进行彻底的更改。The relay service facilitates this by enabling you to securely expose Windows Communication Foundation (WCF) services that reside within a corporate enterprise network to the public cloud, without having to open a firewall connection, or requiring intrusive changes to a corporate network infrastructure.

WCF 中继概念

Azure 中继可用于在现有企业环境中托管 WCF 服务。Azure Relay enables you to host WCF services within your existing enterprise environment. 然后,可以将侦听传入会话和请求这些 WCF 服务的任务委托给在 Azure 内运行的中继服务。You can then delegate listening for incoming sessions and requests to these WCF services to the relay service running within Azure. 这使你能够向 Azure 中运行的应用程序代码或者向移动工作者或 Extranet 合作伙伴环境公开这些服务。This enables you to expose these services to application code running in Azure, or to mobile workers or extranet partner environments. 中继允许精确、安全地控制谁可以访问这些服务。Relay enables you to securely control who can access these services at a fine-grained level. 它提供了一种强大且安全的方式,从现有企业解决方案公开应用程序功能和数据并从云中利用这些功能和数据。It provides a powerful and secure way to expose application functionality and data from your existing enterprise solutions and take advantage of it from the cloud.

本文探讨如何使用 Azure 中继创建 WCF Web 服务,并使用可实现双方之间安全对话的 TCP 通道绑定公开该服务。This article discusses how to use Azure Relay to create a WCF web service, exposed using a TCP channel binding, that implements a secure conversation between two parties.

若要开始在 Azure 中使用服务总线消息实体,必须先使用在 Azure 中唯一的名称创建一个命名空间。To begin using Service Bus messaging entities in Azure, you must first create a namespace with a name that is unique across Azure. 命名空间提供了用于对应用程序中的服务总线资源进行寻址的范围容器。A namespace provides a scoping container for addressing Service Bus resources within your application.

创建命名空间:To create a namespace:

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

  2. 在门户的左侧导航窗格中,依次单击“+ 创建资源”、“集成”和“服务总线”。In the left navigation pane of the portal, click + Create a resource, then click Integration, and then click Service Bus.

  3. 在“创建命名空间” 对话框中,输入命名空间名称。In the Create namespace dialog, enter a namespace name. 系统会立即检查该名称是否可用。The system immediately checks to see if the name is available.

  4. 在确保命名空间名称可用后,选择定价层(基本版或标准版)。After making sure the namespace name is available, choose the pricing tier (Basic or Standard). 如果要使用主题和订阅,请务必选择“标准”。If you want to use topics and subscriptions, make sure to choose Standard. 基本定价层中不支持主题/订阅。Topics/subscriptions are not supported in the Basic pricing tier.

  5. 在“订阅” 字段中,选择要创建命名空间的 Azure 订阅。In the Subscription field, choose an Azure subscription in which to create the namespace.

  6. 在“资源组” 字段中,选择用于放置该命名空间的现有资源组,或者创建一个新资源组。In the Resource group field, choose an existing resource group in which the namespace will live, or create a new one.

  7. 在“位置” 中,选择应在其中托管该命名空间的国家或地区。In Location, choose the country or region in which your namespace should be hosted.

    创建命名空间

  8. 单击“创建” 。Click Create. 系统现已创建命名空间并已将其启用。The system now creates your namespace and enables it. 可能需要等待几分钟,因为系统会为你的帐户配置资源。You might have to wait several minutes as the system provisions resources for your account.

获取管理凭据Obtain the management credentials

创建新的命名空间时,会自动生成一项初始的共享访问签名 (SAS) 规则,将一对主密钥和辅助密钥关联到一起,向每个密钥授予对命名空间的所有资产的完全控制权限。Creating a new namespace automatically generates an initial Shared Access Signature (SAS) rule with an associated pair of primary and secondary keys that each grant full control over all aspects of the namespace. 请参阅服务总线身份验证和授权,了解如何创建更多的规则,对常规的发送者和接收者的权限进行更多限制。See Service Bus authentication and authorization for information about how to create further rules with more constrained rights for regular senders and receivers. 若要复制初始规则,请执行以下步骤:To copy the initial rule, follow these steps:

  1. 单击“所有资源”,然后单击新创建的命名空间名称。Click All resources, then click the newly created namespace name.

  2. 在命名空间窗口中,单击“共享访问策略”。In the namespace window, click Shared access policies.

  3. 在“共享访问策略”屏幕中,单击“RootManageSharedAccessKey”。In the Shared access policies screen, click RootManageSharedAccessKey.

    connection-info

  4. 在“策略: RootManageSharedAccessKey”窗口中,单击“主连接字符串”旁边的“复制”按钮,将连接字符串复制到剪贴板供以后使用。In the Policy: RootManageSharedAccessKey window, click the copy button next to Primary Connection String, to copy the connection string to your clipboard for later use. 将此值粘贴到记事本或其他某个临时位置。Paste this value into Notepad or some other temporary location.

    connection-string

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

获取服务总线 NuGet 包Get the Service Bus NuGet package

服务总线 NuGet 包 是获取服务总线 API 并为应用程序配置所有服务总线依赖项的最简单的方法。The Service Bus NuGet package is the easiest way to get the Service Bus API and to configure your application with all of the Service Bus dependencies. 若要在项目中安装 NuGet 包,请执行以下操作:To install the NuGet package in your project, do the following:

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

  2. 搜索“服务总线”并选择“Microsoft Azure 服务总线”项。Search for "Service Bus" and select the Microsoft Azure Service Bus item. 安装 ”以完成安装,并关闭以下对话框:Click Install to complete the installation, then close the following dialog box:

通过 TCP 公开和使用 SOAP Web 服务Expose and consume a SOAP web service with TCP

要公开现有 WCF SOAP Web 服务以供外部使用,必须更改服务绑定和地址。To expose an existing WCF SOAP web service for external consumption, you must make changes to the service bindings and addresses. 这可能需要更改配置文件或者可能需要更改代码,具体取决于如何设置和配置 WCF 服务。This may require changes to your configuration file or it could require code changes, depending on how you have set up and configured your WCF services. 请注意,WCF 允许对同一服务使用多个网络终结点,因此,可以在添加中继终结点以便进行外部访问的同时保留现有内部终结点。Note that WCF allows you to have multiple network endpoints over the same service, so you can retain the existing internal endpoints while adding relay endpoints for external access at the same time.

在此任务中,将构建一个简单的 WCF 服务并向其添加中继侦听程序。In this task, you build a simple WCF service and add a relay listener to it. 此练习假定你熟悉 Visual Studio,因此不演练创建项目的所有详细信息,This exercise assumes some familiarity with Visual Studio, and therefore does not walk through all the details of creating a project. 而是侧重于代码。Instead, it focuses on the code.

在开始执行这些步骤之前,请完成以下过程以设置环境:Before starting these steps, complete the following procedure to set up your environment:

  1. 在 Visual Studio 中,在解决方案内创建一个包含以下两个项目的控制台应用程序:“客户端”和“服务”。Within Visual Studio, create a console application that contains two projects, "Client" and "Service", within the solution.
  2. 向这两个项目添加服务总线 NuGet 包。Add the Service Bus NuGet package to both projects. 此程序包将向项目添加所有必需的程序集引用。This package adds all the necessary assembly references to your projects.

如何创建服务How to create the service

首先,创建该服务本身。First, create the service itself. 任何 WCF 服务都包含至少三个不同部分:Any WCF service consists of at least three distinct parts:

  • 描述交换哪些信息以及将调用哪些操作的协定的定义。Definition of a contract that describes what messages are exchanged and what operations are to be invoked.
  • 上述协定的实施方案。Implementation of that contract.
  • 托管 WCF 服务并公开多个终结点的主机。Host that hosts the WCF service and exposes several endpoints.

本部分中的代码示例涵盖了其中的每个组成部分。The code examples in this section address each of these components.

协定定义用于添加两个数字并返回相应结果的单个操作 AddNumbersThe contract defines a single operation, AddNumbers, that adds two numbers and returns the result. IProblemSolverChannel 接口使客户端能够更轻松地管理代理生存期。The IProblemSolverChannel interface enables the client to more easily manage the proxy lifetime. 创建这样一个接口被认为是最佳实践。Creating such an interface is considered a best practice. 最好将此协定定义放入单独的文件中,以便可从“客户端”和“服务”两个项目中引用该文件,但也可以将代码复制到这两个项目中。It's a good idea to put this contract definition into a separate file so that you can reference that file from both your "Client" and "Service" projects, but you can also copy the code into both projects.

using System.ServiceModel;

[ServiceContract(Namespace = "urn:ps")]
interface IProblemSolver
{
    [OperationContract]
    int AddNumbers(int a, int b);
}

interface IProblemSolverChannel : IProblemSolver, IClientChannel {}

协定到位后,可以按以下方式进行实施:With the contract in place, the implementation is as follows:

class ProblemSolver : IProblemSolver
{
    public int AddNumbers(int a, int b)
    {
        return a + b;
    }
}

以编程方式配置服务主机Configure a service host programmatically

协定和实施完成后,现在就可以托管服务了。With the contract and implementation in place, you can now host the service. 托管发生在 System.ServiceModel.ServiceHost 对象内,该对象负责管理服务实例并托管侦听消息的终结点。Hosting occurs inside a System.ServiceModel.ServiceHost object, which takes care of managing instances of the service and hosts the endpoints that listen for messages. 以下代码使用常规的本地终结点和中继终结点配置服务,以便并列展示内部和外部终结点的外观。The following code configures the service with both a regular local endpoint and a relay endpoint to illustrate the appearance, side by side, of internal and external endpoints. 将字符串 namespace 替换为命名空间名称,并将 yourKey 替换为前面的设置步骤中获取的 SAS 密钥。Replace the string namespace with your namespace name and yourKey with the SAS key that you obtained in the previous setup step.

ServiceHost sh = new ServiceHost(typeof(ProblemSolver));

sh.AddServiceEndpoint(
   typeof (IProblemSolver), new NetTcpBinding(),
   "net.tcp://localhost:9358/solver");

sh.AddServiceEndpoint(
   typeof(IProblemSolver), new NetTcpRelayBinding(),
   ServiceBusEnvironment.CreateServiceUri("sb", "namespace", "solver"))
    .Behaviors.Add(new TransportClientEndpointBehavior {
          TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("RootManageSharedAccessKey", "<yourKey>")});

sh.Open();

Console.WriteLine("Press ENTER to close");
Console.ReadLine();

sh.Close();

在本示例中,你创建两个位于同一协定实施中的终结点。In the example, you create two endpoints that are on the same contract implementation. 一个是本地的,一个通过 Azure 中继进行投影。One is local and one is projected through Azure Relay. 两者之间的主要区别是绑定;本地终结点使用 NetTcpBinding,而中继终结点和地址使用 NetTcpRelayBindingThe key differences between them are the bindings; NetTcpBinding for the local one and NetTcpRelayBinding for the relay endpoint and the addresses. 本地终结点有一个使用不同端口的本地网络地址。The local endpoint has a local network address with a distinct port. 中继终结点有一个由字符串 sb、命名空间名称、路径“solver”组成的终结点地址。The relay endpoint has an endpoint address composed of the string sb, your namespace name, and the path "solver." 这会生成 URI sb://[serviceNamespace].servicebus.windows.net/solver,将服务终结点标识为具有完全限定的外部 DNS 名称的服务总线(中继)TCP 终结点。This results in the URI sb://[serviceNamespace].servicebus.windows.net/solver, identifying the service endpoint as a Service Bus (relay) TCP endpoint with a fully qualified external DNS name. 如果将替换占位符的代码放入Main服务应用程序的 函数中,则会获得功能服务。If you place the code replacing the placeholders into the Main function of the Service application, you will have a functional service. 如果希望服务专门侦听中继,请删除本地终结点声明。If you want your service to listen exclusively on the relay, remove the local endpoint declaration.

在 App.config 文件中配置服务主机Configure a service host in the App.config file

还可以使用 App.config 文件配置主机。You can also configure the host using the App.config file. 在此情况下,服务托管代码如以下示例所示。The service hosting code in this case appears in the next example.

ServiceHost sh = new ServiceHost(typeof(ProblemSolver));
sh.Open();
Console.WriteLine("Press ENTER to close");
Console.ReadLine();
sh.Close();

终结点定义移到 App.config 文件中。The endpoint definitions move into the App.config file. NuGet 包已向 App.config 文件添加一系列定义,这些定义是 Azure 中继必需的配置扩展。The NuGet package has already added a range of definitions to the App.config file, which are the required configuration extensions for Azure Relay. 以下示例(与前面的代码完全等效)应该紧靠在 system.serviceModel 元素的下面。The following example, which is the exact equivalent of the previous code, should appear directly beneath the system.serviceModel element. 此代码示例假设项目 C# 命名空间名命名为“Service”。This code example assumes that your project C# namespace is named Service. 将占位符替换为中继命名空间名称和 SAS 密钥。Replace the placeholders with your relay namespace name and SAS key.

<services>
    <service name="Service.ProblemSolver">
        <endpoint contract="Service.IProblemSolver"
                  binding="netTcpBinding"
                  address="net.tcp://localhost:9358/solver"/>
        <endpoint contract="Service.IProblemSolver"
                  binding="netTcpRelayBinding"
                  address="sb://<namespaceName>.servicebus.chinacloudapi.cn/solver"
                  behaviorConfiguration="sbTokenProvider"/>
    </service>
</services>
<behaviors>
    <endpointBehaviors>
        <behavior name="sbTokenProvider">
            <transportClientEndpointBehavior>
                <tokenProvider>
                    <sharedAccessSignature keyName="RootManageSharedAccessKey" key="<yourKey>" />
                </tokenProvider>
            </transportClientEndpointBehavior>
        </behavior>
    </endpointBehaviors>
</behaviors>

进行这些更改后,该服务将像以前一样启动,但具有两个活动终结点:一个位于本地,一个在云中侦听。After you make these changes, the service starts as it did before, but with two live endpoints: one local and one listening in the cloud.

创建客户端Create the client

以编程方式配置客户端Configure a client programmatically

要使用该服务,可以使用 ChannelFactory 对象构造 WCF 客户端。To consume the service, you can construct a WCF client using a ChannelFactory object. 服务总线使用通过 ACS 实现的基于令牌的安全模型。Service Bus uses a token-based security model implemented using SAS. TokenProvider 类代表具有内置工厂方法的安全令牌提供程序,这些方法可返回一些众所周知的令牌提供程序。The TokenProvider class represents a security token provider with built-in factory methods that return some well-known token providers. 以下示例使用 CreateSharedAccessSignatureTokenProvider 方法处理相应 SAS 令牌的获取。The following example uses the CreateSharedAccessSignatureTokenProvider method to handle the acquisition of the appropriate SAS token. 名称和密钥是根据上一部分所述从门户获取的凭据。The name and key are those obtained from the portal as described in the previous section.

首先,在客户端项目中引用服务中的 IProblemSolver 约定代码或将其复制到客户端项目中。First, reference or copy the IProblemSolver contract code from the service into your client project.

然后,替换客户端的 Main 方法中的代码,再次将占位符文本替换为中继命名空间和 SAS 密钥。Then, replace the code in the Main method of the client, again replacing the placeholder text with your relay namespace and SAS key.

var cf = new ChannelFactory<IProblemSolverChannel>(
    new NetTcpRelayBinding(),
    new EndpointAddress(ServiceBusEnvironment.CreateServiceUri("sb", "<namespaceName>", "solver")));

cf.Endpoint.Behaviors.Add(new TransportClientEndpointBehavior
            { TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("RootManageSharedAccessKey","<yourKey>") });

using (var ch = cf.CreateChannel())
{
    Console.WriteLine(ch.AddNumbers(4, 5));
}

现在可以生成客户端和服务,运行它们(首先运行服务),客户端调用该服务并输出 9You can now build the client and the service, run them (run the service first), and the client calls the service and prints 9. 可以在不同计算机上,甚至跨网络运行客户端和服务器,通信仍将进行。You can run the client and server on different machines, even across networks, and the communication will still work. 客户端代码还可以在云中或在本地运行。The client code can also run in the cloud or locally.

在 App.config 文件中配置客户端Configure a client in the App.config file

以下代码介绍了如何使用 App.config 文件配置客户端。The following code shows how to configure the client using the App.config file.

var cf = new ChannelFactory<IProblemSolverChannel>("solver");
using (var ch = cf.CreateChannel())
{
    Console.WriteLine(ch.AddNumbers(4, 5));
}

终结点定义移到 App.config 文件中。The endpoint definitions move into the App.config file. 以下示例(与前面列出的代码相同)应紧接在 <system.serviceModel> 元素下面。The following example, which is the same as the code listed previously, should appear directly beneath the <system.serviceModel> element. 在此,与之前一样,必须将占位符替换为中继命名空间和 SAS 密钥。Here, as before, you must replace the placeholders with your relay namespace and SAS key.

<client>
    <endpoint name="solver" contract="Service.IProblemSolver"
              binding="netTcpRelayBinding"
              address="sb://<namespaceName>.servicebus.chinacloudapi.cn/solver"
              behaviorConfiguration="sbTokenProvider"/>
</client>
<behaviors>
    <endpointBehaviors>
        <behavior name="sbTokenProvider">
            <transportClientEndpointBehavior>
                <tokenProvider>
                    <sharedAccessSignature keyName="RootManageSharedAccessKey" key="<yourKey>" />
                </tokenProvider>
            </transportClientEndpointBehavior>
        </behavior>
    </endpointBehaviors>
</behaviors>

后续步骤Next steps

现已了解有关 Azure 中继的基础知识,请单击以下链接了解详细信息。Now that you've learned the basics of Azure Relay, follow these links to learn more.