Web 服务、远程处理和 Pocket PC

发布日期: 7/30/2004 | 更新日期: 7/30/2004

Eric Gunnerson
Microsoft Corporation

摘要:Eric Gunnerson 通过为媒体播放机创建远程控件,论述了 Web 服务和远程处理的复杂机制,然后应用其中的原理将该应用程序移植到他自己的 Pocket PC 上。

*

下载与本文相关的示例代码

我又一次发现自己正在飞机上旅行,虽然这一次我要去毛伊岛度过一个期待已久的假期。为什么度假时我还要因为工作的原因而撰写专栏文章,这点令我身旁的旅客感到一丝困惑,而我承认这同样也让我感到有些困惑,但长途飞行的确是写文章或整理一些代码的好时机。与许多其他时候不同,在飞机上完全保持沉默三个或四个小时被视为值得注意的行为,因此我戴上索尼消音耳机,彻底沉浸在自己的世界里。

我最近已经在 http://blogs.gotdotnet.com/ericgu 开始撰写自己的网络日记。我曾经犹豫过一段时间,因为我不认为人们会关心我最终会写出什么东西,但有两个因素彻底让我下定了决心。第一个因素是有一些题目确实不值得为之撰写专栏,因此我决定不为它们写任何东西。第二个因素是 MSDN 评论系统并不能让我产生兴趣。我这样说的意思是,如果我对完善的评论系统提出一系列要求,MSDN 系统将无法满足其中任何一项要求,除了或许可以在 Web 页上输入评论以外。网络日记是我可以掌握的一种东西,因此您和我都可以在感觉合适的时候进行评论,并留有一定的回旋余地。

当这个专栏问世时,我会在我的网络日记中通报这一消息。如果您有与该专栏有关的意见或问题,请将它们添加到网络日记项中。

我还可以将我的膝上型电脑中的代码发布到我的网络日记中,以便与大家共享。好了,让我们开始讨论本月的主题吧。

在程序间通信

过去几年来,我们一直处于 Microsoft 为 Web 服务所发起的营销宣传的包围之中,但是它已经使人对不同的程序间的通信技术产生了错误的印象。Web 服务对于某些场合确实很好,但其他一些技术也是值得考虑的。

其中最重要的一种技术是 .NET 远程处理,当您在连接的两端都使用 .NET 时,该技术将很有用。您可以以透明方式使用任何类型,而不会被限制为 SOAP 类型。

简单的远程处理示例

作为示例,我将为媒体播放机程序生成一个远程控件。我将在一台计算机上运行该播放机程序,而从另一台计算机上远程控制该程序。

第一步是定义一个接口,以描述可以做的事情。服务器端对象将实现该接口,而客户端将基于该接口执行调用。严格说来,我不需要做这项工作。如果我在客户端使用服务器端类型,.NET 远程处理仍然可以很好地工作,但如果我那样做的话,客户端将需要访问服务器端对象,而这样并不好。所以,我将定义一个接口,然后将其放在类库对象中:

namespace RemotingLibrary
{
   public interface IPlayer
   {
      void Play();
      void Pause();
      string GetCurrentSong();
   }
}

下一步,我将在服务器上工作。我知道我需要一个对象来实现该接口,因此我从编写该对象开始。

using System;
using System.Runtime.Remoting;
using RemotingLibrary;

namespace RemotingServer
{
   public class MyRemote: MarshalByRefObject, IPlayer
   {
      public MyRemote()
      {
      }

      public void Pause()
      {
         // TODO:  Add MyRemote.Pause implementation
      }

      public void Play()
      {
         // TODO:  Add MyRemote.Play implementation
      }

      public string GetCurrentSong()
      {
         return "Devo - Girl U Want";
      }
   }
}

我已经为 GetCurrentSong() 植入了一些测试代码,从而可以检查我所得到的结果。注意,MyRemote 类型是从 MarshalByRefObject 派生的。要想从远程处理机制中使用对象,这是其中一项要求。这一点比较遗憾,因为这意味着我不能对该对象使用我自己的基类。

接下来,需要编写服务器代码。我将稍经修改的 MSDN 示例作为服务器代码的基础。

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace RemotingServer
{
   class Server
   {
      public static void Main()
      {
         TcpChannel m_TcpChan = new TcpChannel(9999);
         ChannelServices.RegisterChannel(m_TcpChan);
         RemotingConfiguration.RegisterWellKnownServiceType(
               typeof(MyRemote),
               "MyRemote", WellKnownObjectMode.SingleCall);
         System.Console.WriteLine("Press ENTER to quit");
         System.Console.ReadLine();
      }
   }
}

这些代码似乎要比想象中的简单。在端口 9999 上打开了一个 TCP 信道,注册该信道,然后将我的类型注册为可以远程使用的类型。完成这一工作后,运行库将为我处理所有事情,所以我只须静静地等待。

要编写的最后一部分代码是客户端代码。

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using RemotingLibrary;
namespace RemotingClient
{
   class RemoteClient
   {
      public static void Main()
      {
         TcpChannel tcpChan = new TcpChannel();
         ChannelServices.RegisterChannel(tcpChan);
         IPlayer player = (IPlayer)
            Activator.GetObject(typeof(IPlayer),
            "tcp://localhost:9999/MyRemote");
         Console.WriteLine(player.GetCurrentSong());
      }
   }
}

该类引用了我在前面创建的接口库。它创建了一个 TcpChannel 并将其注册,但不是在特定端口上创建。然后,它使用 Activator 类连接到远程对象,调用 GetCurrentSong(),并写出返回值。完整示例位于 RemotingToAType 子目录中。

如果我愿意,我可以通过更改到 http 信道,将我的示例转换成 Web 服务的样子。该示例位于 RemoteToATypeSoap 子目录中。您可以通过向字符串末尾添加“?wsdl”,将该服务器添加为 Web 引用,那样它就能像任何其他 Web 服务一样工作。不过,在 MSDN 中有一条重要的注意事项,声明虽然这对 WSDL 有效,但无法创建 XSD,因而可能无法对将来的工具起作用。

这是一个很不错的示例,尽管实际上它并没有让我向目标前行多少,因为所创建的对象与正在运行的应用程序没有任何联系。我希望远程控制服务器上正在运行的实例,而不是刚刚创建的实例。换一种说法,就是我不希望这是一个无状态模型。

远程控制实例

在我编写第一个版本后,我开始研究以便搞清楚如何对一个实例做同样的事情。我试图查找名为 RegisterInstance 的例程,或者与此类似的东西,但我的搜索失败了。

在使用 Google 进行了一番千辛万苦的搜索之后,我发现了一个这样的例程,但它的名称为“Marshal”。这个名称在我的文章中不是太好,因为它与我要完成的工作没有任何关系,但既然我知道了这个例程,我就可以修改我的代码。为此,我需要对 Server 类进行一些小的改动:

      public static void Main()
      {
         MyRemote myRemote = new MyRemote("The Scorpions - Always Somewhere");
         TcpChannel m_TcpChan = new TcpChannel(9999);
         ChannelServices.RegisterChannel(m_TcpChan);
         RemotingServices.Marshal(myRemote, "MyRemote");
         System.Console.WriteLine("Press ENTER to quit");
         System.Console.ReadLine();
      }

在这些代码中,我创建了 MyRemote 类的一个实例,然后使用 Marshal 注册了该实例。我还修改了 MyRemote 类,使它采用歌曲名称作为参数,这只是为了表明我确实在与该实例进行通信。

这些代码非常不错,简单并且有效,它们位于 RemotingToAnInstance 目录中。该示例仅仅涉及到远程处理技术的一些粗浅内容,实际上,在使用什么样的信道、使用什么样的安全性以及何时执行身份验证等方面都具有相当大的灵活性。如果使用 ASP.NET 作为宿主,其中大多数事情都更加容易做到。

返回页首返回页首

体会在 Pocket PC 中的应用

我有一台 Toshiba 的 Pocket PC,可以充当此类应用程序的理想客户端。它有漂亮的彩色屏幕,内置 802.11 无线协议,并且我可以使用 C# 进行编程。

遗憾的是,如果您阅读 .NET 框架压缩版的文档,您会发现它“不”包含对远程处理的支持,因此该方法行不通。Web 服务可能是一种方法,但我的目标之一是能够从服务器向客户端传递事件,但在客户端中没有事件支持。我所在工作组中的其他某位项目经理想到了一个主意,就是调用 Web 服务,并且让它在事件发生时返回。这个主意很好而且确实有效,因此我编写了一个示例,从 Pocket PC 中调用 Web 服务。

进行 Web 服务调用时,要产生大量的开销 — 转换到 SOAP 格式,在网上发送调用,取回 SOAP 格式的信息,然后进行反向转换。对于我的 Pocket PC,这导致每秒钟执行三个调用。这不太符合我的期望。

因此,Web 服务也被我放弃了。当高级服务无法完成所需的工作时,有时需要深入到更低级的服务。在这种情况下,这意味着使用原始套接字而不是远程处理或 Web 服务。但该主题要留待下一期专栏加以讨论了。

Eric Gunnerson 是 Visual C# 组的程序经理,以前曾是 C# 语言设计组的成员,著有 A Programmer's Introduction to C#, 2nd Edition。他从事编程工作已有很长时间,积累了丰富的编程经验,他熟悉 8 英寸磁盘并且还曾经亲手装过磁带。在业余时间内,他正准备撰写一部专著,探讨易服现象的兴起与二十世纪七十年代的喜剧短片之间的关系。

转到原英文页面


返回页首返回页首