65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 .NET Web 服务进行异步通信

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (28投票s)

2002 年 6 月 23 日

7分钟阅读

viewsIcon

200095

downloadIcon

868

解释如何与 .NET Web 服务进行异步通信

引言

XML Web 服务的引入重新定义了软件组件的分布式方式,以及客户端如何使用其功能。事实上,Web 服务基于 SOAP 和 HTTP 等开放标准协议,有望成为跨异构系统使用功能和提供服务的主要手段之一。这似乎与 HTML 所带来的效果非常相似。

然而,在本文中,我将重点介绍如何与 Web 服务进行异步通信,并具体说明如何使用 Microsoft .NET 框架实现这一目标。因此,熟悉 .NET 框架和 C#(因为源代码将用 C# 编写)将对您大有裨益。话虽如此,让我们开始吧。

WSPrime 简介

我一直坚信通过代码来可视化和理解概念,本文也不例外。在这里,我们有一个 Web 服务,它将返回到指定数字为止找到的素数个数。例如,如果传入 10 作为参数,那么 Web 服务将返回 4,因为到 10 为止有四个素数,即 2、3、5 和 7。以下是 Web 服务的源代码

<%@ WebService Language="C#" class="WSPrime" %>
using System;
using System.Web.Services;
public class WSPrime : WebService
{
// This method returns the number of prime number lying between
// 2 and num.
[WebMethod]
public int Prime(int num)
{
         int iCount=0;
         for(int i=2;i<num;i++)
         {
                bool bPrime=true;
                for(int j=2;j<i;j++)
                {
                     // is this number prime ?
                     if (i%j==0)
                     {
                          // nope.. it isn't...
                          bPrime=false;
                          break;
                      }
                 }

                 if (bPrime==true)
                     iCount++;
         }

         // return the count..
         return iCount;
}
}

该 Web 服务只公开了一个方法 Prime,它接受一个整数作为输入,并计算到指定数字为止的素数个数。完成后,将计数返回给调用者。计算素数和检查数字是否为素数的逻辑相对简单。

我将此 Web 服务安装在我本地机器上的虚拟文件夹 wstester 中,可以使用浏览器通过 https:///wstester/wstester.asmx 调用它,其中 wstester.asmx 是 Web 服务源文件。接下来,我使用 .NET SDK 安装附带的 WSDL 工具创建其代理,如下所示

wsdl /l:cs https:///wstester/wstester.asmx

这将生成 wsprime.cs 文件(以其中包含的类命名)。对于那些不知道的人,/l 参数用于指定代理源代码将使用的语言,cs 指定使用 C#。现在是本文的重点:深入了解代理!

WSPrime.cs 内部

如果您打开创建的代理文件,它将如下所示

//----------------------------------------------------------------------------

// <autogenerated>

//     This code was generated by a tool.

//     Runtime Version: 1.0.3705.0

//

//     Changes to this file may cause incorrect behavior and will be lost if 

//     the code is regenerated.

// </autogenerated>

//----------------------------------------------------------------------------

// 

// This source code was auto-generated by wsdl, Version=1.0.3705.0.

// 

using System.Diagnostics;
using System.Xml.Serialization;
using System;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.Web.Services;

/// <remarks/>

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="WSPrimeSoap", 
                                      Namespace="http://tempuri.org/")]
public class WSPrime : System.Web.Services.Protocols.SoapHttpClientProtocol {

/// <remarks/>

public WSPrime() {
this.Url = "https:///wstester/wstester.asmx";
}

/// <remarks/>

[System.Web.Services.Protocols.SoapDocumentMethodAttribute
    ("http://tempuri.org/Prime", RequestNamespace="http://tempuri.org/", 
    ResponseNamespace="http://tempuri.org/", 
    Use=System.Web.Services.Description.SoapBindingUse.Literal, 
    ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public int Prime(int num) {
object[] results = this.Invoke("Prime", new object[] {
num});
return ((int)(results[0]));
}

/// <remarks/>

public System.IAsyncResult BeginPrime(int num, 
    System.AsyncCallback callback, object asyncState) {
return this.BeginInvoke("Prime", new object[] {
num}, callback, asyncState);
}

/// <remarks/>

public int EndPrime(System.IAsyncResult asyncResult) {
object[] results = this.EndInvoke(asyncResult);
return ((int)(results[0]));
}
}

构造函数将 URL 属性初始化为指向 Web 服务的位置。但是,我们关注的焦点是 Prime 方法的三个版本

第一个版本,名为 Prime,是对实际 Web 服务方法的同步调用,并且在 Web 服务返回之前将一直阻塞。

第二个版本是异步版本,由 BeginPrimeEndPrime 方法组成。

实际上,对于 Web 服务中的每个公开方法,WSDL 工具都会创建这两个版本的方法。异步版本由两个方法组成,方法名称前缀为 BeginEnd。通常,开发人员会忽略方法的异步版本,并在实例化 Web 服务代理类后使用同步版本。使用异步版本需要开发人员付出更多努力,但可以使调用者在调用 Web 服务方法时免于阻塞。一旦使用代理的 Begin 方法发出调用,Begin 方法将请求发送到 Web 服务,然后立即返回给调用者,这允许调用者执行任何其他工作。一旦 Web 服务方法完成其工作,它将回调 Begin 方法的调用者

那么,您现在心中会有一个问题:“Web 服务将如何通知调用者?”。嗯,如果您仔细观察上面的示例代理源代码,BeginPrime 方法接受三个参数,而不是 Web 服务方法实际接受的一个参数。倒数第二个参数 System.AsyncCallback 回调是一个委托对象,当 Web 服务完成时,它将通知并因此回调。最后一个参数 object asyncState 是您可能希望发送给委托以供其工作时(当 Web 服务回调时)的任何状态信息。

现在我们已经清楚了一些基本概念,让我们来看看上面示例 Web 服务的异步客户端的实现

using System;
using System.Runtime.Remoting.Messaging;
public class TestProxy
{
private static bool bEnd=false;

// our delegate which will be called back to notify that work is done.

public static void PrimeDoneCallback(IAsyncResult arResult)
{
// get the "this" pointer that was passed...

WSPrime wsp=(WSPrime)arResult.AsyncState;

// call "EndPrime" and get the result..

int iCount=wsp.EndPrime(arResult);

/// show the result of the webservice call..

Console.WriteLine("Async. WebService call found {0} primes.", iCount);

// set flag so that application can terminate...

bEnd=true;
}

public static void Main()
{
// take inputs from the user...

int iNum, iCount;
Console.WriteLine("Enter a number till which primes have to be counted: ");
iNum=Convert.ToInt32(Console.ReadLine());
if (iNum<2)
{
Console.WriteLine("Number should be >=2.");
return;
}

// instantiate the webservice proxy class

WSPrime wsp = new WSPrime();

// tell the user what we are doing...

Console.WriteLine("Calling WebService Asynchronously...\n");

// do the async. call... pass it a "WSPrime" object as a "this" object

AsyncCallback acb = new AsyncCallback(TestProxy.PrimeDoneCallback);
wsp.BeginPrime(iNum,acb,wsp);

// so, while the async. call is on way.. lets calculate it on our own

// as well...

Console.WriteLine("Calculating number of primes myself as well...\n");

iCount=0;
for(int i=2;i<=iNum;i++)
{
   bool bPrime=true;
   for(int j=2;j<i;j++)
   {
      if (i%j==0)
      {
         // this isn't a prime number...

         bPrime=false;
         break;
      }
   }

   // increment count if prime number..

   if (bPrime==true)
       iCount++;
}

// tell our count to user...

Console.WriteLine("I found {0} primes",iCount);

// wait for webservice to end...

Console.WriteLine("Waiting for webservice to end..");
while(bEnd==false);
Console.WriteLine("Over!");
}
};

让我们以应用程序流的方式逻辑地遍历此源代码。首先,将要计数素数的数字作为输入,并确保它大于 1。接下来,实例化 WSPrime 类的对象 wsp,如果您从上面的源代码中回忆,它是 Web 服务代理类。

接下来,由于我们必须异步调用 Web 服务方法,因此必须使用 BeginPrime,我们创建了一个 AsyncCallback 委托对象(因此,我们指定使用 System.Runtime.Remoting.Messaging,因为此命名空间包含 AsyncCallback 类),并将要回调的方法的地址传递给它,即 TestProxy 类的静态 PrimeDoneCallback 方法,它是我们的主应用程序类。委托被定义为类的静态方法,但可以很容易地更改为实例方法。

最后,在 wsp 对象上调用 BeginPrime 方法,并将要计算素数的数字作为第一个参数传递,后跟回调委托对象 acb 和指向代理类对象的指针。我们为什么要传递指向代理类对象的指针?嗯,事实是,每当我们必须异步调用 Web 服务方法时,我们都必须以 Begin/End 方法对的形式进行。由于 BeginPrime 方法是在 Main 中调用的,并且当 Web 服务方法完成其工作时,控制将返回到我们的回调方法,因此我们需要在回调方法中拥有代理类对象,以便可以调用 EndPrime 方法并获取 Web 服务方法调用的结果。因此,代理类对象 wsp 作为最后一个参数传递给 BeginPrime

BeginPrime 方法立即返回,这使得调用者(即 Main)可以执行其他任务。因此,在上面的示例客户端中,我已将 Main 编程为计算到用户指定数字为止的素数个数。当然,您可以在此处执行任何操作,但我选择这样做是为了输出可以显示比较结果。完成工作后,将输出显示给用户,并且应用程序在我们的应用程序类的静态变量上阻塞,以便我们的应用程序在 Web 服务发送回调之前不会退出。

当 Web 服务发送回调时,将调用 PrimeDoneCallback 方法。此方法只接收一个参数,类型为 IAsyncResult。我们从 AsyncState 属性中提取作为参数发送给 BeginPrime 方法调用的代理类对象的对象指针,并适当地将其装箱。接下来是在代理类对象上调用 EndPrime 方法,并传递委托方法接收到的 IAsyncResult 参数。EndPrime 方法返回 Web 服务调用的结果,然后将其显示为输出,并将标志变量 bEnd 设置为 true,以便阻塞在该标志上的 Main 方法被释放,应用程序可以退出。

最后

要编译代理源代码,请在 VS.NET 命令提示符下执行以下命令

csc /t:library wsprime.cs

接下来,通过引用 Web 服务代理程序集来编译 Web 服务客户端,如下所示

csc /r:wsprime.dll wsprime_client.cs

结论

从上面的示例可以看出,通过多做一点努力,开发人员可以从 Web 服务中的异步回调中获益,并使其应用程序更具响应性和可伸缩性。令人惊讶的是,大量的开发社区没有利用这一事实,导致异步 Web 服务通信未充分利用。

© . All rights reserved.