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

远程处理绝对入门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (81投票s)

2002 年 4 月 5 日

CPOL

5分钟阅读

viewsIcon

361515

downloadIcon

1

介绍 .NET Remoting 以及如何使用简单的代码片段来处理异步回调。

引言

Remoting 允许您远程处理对象。它比基于 SOAP 的 Web 服务效率更高,但显然只能在内网中使用。在互联网上,您会遇到防火墙问题,并且整体安全性不足。

用非常简单的话来说,您有一个在某台机器上运行的客户端程序,它调用在另一台机器上实例化的对象的函数。让我们来看一些非常简短而精炼的代码片段。通常,我认为通过查看一个小代码片段比阅读一篇 2000 字的文章来理解一个概念更容易,后者会为您提供各种复杂的理论。当然,任何额外的理论知识总会在很多方面有所帮助。

界面

我们首先创建一个名为 MyInterface 的接口。然后将其编译成一个 dll。稍后我们将从该接口派生我们的远程类。现在,这样做的好处是,在客户端机器上我们可以使用这个接口。否则,我们必须直接从客户端机器使用该类,这几乎使整个练习有点徒劳。

// csc /t:library MyInterface.cs

public interface MyInterface
{
    int FunctionOne(string str);
}

对象

现在我们的接口已经准备好了,让我们创建将在远程机器上调用的类。我们从 MarshalByRefObjectMyInterface 派生此类。任何派生自 MarshalByRefObject 的类都允许远程客户端调用其方法,这正是我们在这里要做的。

// csc /t:library /r:MyInterface.dll RemoteObject.cs

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;

public class MyRemoteClass : MarshalByRefObject,MyInterface
{
    public int FunctionOne(string str)
    {		
        return str.Length;
    }
}

服务器

现在,让我们编写远程服务器。

// csc /r:RemoteObject.dll RemoteServer.cs
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

public class MyServer
{
    public static void Main()
    {
        TcpChannel m_TcpChan = new TcpChannel(9999);
        ChannelServices.RegisterChannel(m_TcpChan);
        RemotingConfiguration.RegisterWellKnownServiceType(
            Type.GetType("MyRemoteClass,RemoteObject"),
            "FirstRemote",WellKnownObjectMode.SingleCall);
        System.Console.WriteLine("Press ENTER to quit");
        System.Console.ReadLine();
    }
}

首先,我们创建一个 TcpChannel 对象,用于在我们的 Remoting 边界上传输消息。正如您所见,我还选择了 9999 作为监听的 TCP 端口。现在您可以理解这为什么会导致防火墙遇到很多问题。我们使用 ChannelServices.RegisterChannel 将我们的 TcpChannel 注册到通道服务。不要问我那个函数里面具体是什么。我唯一能弄清楚的是,如果您不注册通道,Remoting 将无法工作。现在,我们使用 RemotingConfiguration.RegisterWellKnownServiceType 将我们的 MyRemoteClass 对象注册为一个已知类型。现在这个对象就可以被远程访问了。

我第一次看到这么长的函数名时,以为是个笑话。但 .NET 框架有成千上万的函数和类,所以我猜他们别无选择。我猜这比调用 __rwkst(...) 要好得多。我在这里使用了 FirstRemote 这个词,它将作为客户端访问远程对象时使用的 URL 的一部分。我在这里指定了 SingleCall 模式,这意味着每次远程调用都会创建一个新实例。我本可以使用 Singleton,在这种情况下,将实例化一个对象并由所有连接的客户端使用。我想如果这是一篇高级文章(它不是)由一名高级程序员(我不是)撰写,会有一些关于这两种模式之间区别的解释。

客户端

好的,现在在一台机器上运行您的服务器。现在让我们看看客户端,它可以运行在同一台机器上或另一台机器上。我不得不在同一台机器上运行它,因为有一些复杂的技术原因。老实说,复杂的技术原因是我家里只有一台机器。

// csc /r:MyInterface.dll client.cs

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

class MyClient
{
    public static void Main()
    {
        TcpChannel m_TcpChan = new TcpChannel();
        ChannelServices.RegisterChannel(m_TcpChan);
        MyInterface m_RemoteObject = (MyInterface)
            Activator.GetObject(typeof(MyInterface),
            "tcp://earth:9999/FirstRemote");
        Console.WriteLine(m_RemoteObject.FunctionOne("Nish"));
    }
}

与服务器一样,我们也有一个 TcpChannel 对象,但在这种情况下,我们不指定端口。我们还使用 ChannelServices.RegisterChannel 来注册通道。我们使用 Activator.GetObject 来获取 MyInterface 对象的实例。正如您所看到的,我在 URL 中使用了主机名“earth”,因为这是我的机器的主机名。在您的案例中,您需要将其替换为远程机器的名称或其 IP 地址。构成 URL 一部分的“FirstRemote”这个词是我在服务器上传递给 RemotingConfiguration.RegisterWellKnownServiceType 的内容。现在我已经得到了我的对象,我可以调用它的成员函数了。嗯,这并不难,不是吗?

异步回调

在客户端中,我们对远程对象使用了同步调用。这样做的缺点是,如果函数执行时间很长,我们的程序就会挂起,直到函数返回。当然,每次想要访问远程方法时,您可以启动一个工作线程,但这效率不高。而且,如果您碰巧将共享数据传递给方法,并且也从方法中获取一些共享数据,您就需要小心处理线程同步。好消息是,您不必做所有这些复杂的事情。您可以使用异步回调。

// csc asyncclient.cs /r:MyInterface.dll

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Messaging;

class MyClient
{
    public delegate int MyDelegate(string s);
    public static void Main()
    {
        TcpChannel m_TcpChan = new TcpChannel();
        ChannelServices.RegisterChannel(m_TcpChan);
        MyInterface m_RemoteObject = (MyInterface)
            Activator.GetObject(typeof(MyInterface),
            "tcp://earth:9999/FirstRemote");    
        MyDelegate m_delegate = 
            new MyDelegate(m_RemoteObject.FunctionOne);
        m_delegate.BeginInvoke("Amazing", 
            new AsyncCallback(MyCallBack),null);
        System.Console.WriteLine("Press ENTER to quit");
        System.Console.ReadLine();
    }

    public static void MyCallBack(IAsyncResult ar)
    {
        int l = ((MyDelegate)((AsyncResult)ar).AsyncDelegate).EndInvoke(ar);
        Console.WriteLine(l);
    }
}

这里我们有两个 委托。委托是类型安全的函数指针。第一个委托是我们创建的 MyDelegate。我们将它指向我们需要从远程对象访问的函数。现在我们调用 BeginInvoke 函数。第一个参数是我们传递给实际函数的参数。第二个参数是另一个类型为 AsyncCallback 的委托。现在发生的情况是,远程函数被调用,但控制立即返回,我们的程序不会被阻塞。稍后,当远程对象完成函数的执行时,我们的回调会被调用,并传入一个 IAsyncResult 参数,该参数封装了异步委托上异步操作的结果。我们使用 AsyncDelegate 属性来获取我们的委托,并将其转换为 MyDelegate,然后对其调用 EndInvoke 来获取远程执行函数的結果。您必须记住,远程函数已经终止,它的终止与 EndInvoke 无关。EndInvoke 只是将操作的结果返回给我们。

结论

我真心希望阅读本文的初学者能从中受益。Remoting 还有很多内容,如果我学到更多东西,我会在这里发布更多文章。

© . All rights reserved.