19

在 .NET 远程处理中,RemotingConfiguration.RegisterWellKnownServiceType 和 RemotingServices.Marshal 有什么区别?

我想要做的是在 Windows 服务中创建一个对象,然后将其作为远程处理对象放入,并让 Windows 服务和客户端都对远程处理对象起作用。

我认为下面的代码可以实现这一点。

FooRemoting foo = new FooRemoting();

RemotingConfiguration.RegisterWellKnownServiceType(typeof(FooRemoting), serverName, WellKnownObjectMode.Singleton);
RemotingServices.Marshal(foo);
4

3 回答 3

20

这就是我发现的。

RemotingConfiguration.RegisterWellKnownServiceType(typeof(FooRemoting), 
          serverName, WellKnownObjectMode.Singleton);

RegisterWellKnownServiceType 将创建该对象并使其成为任何使用它的客户端的 Singleton,但不会创建服务器的引用。该对象在客户端请求之前不会创建,并且相同的对象用于任何其他客户端。

RemotingServices.Marshal(foo);

Marshal 将注册一个由服务器创建的对象,在本例中是一个 Windows 服务。然后服务器将引用该对象,客户端将使用相同的对象。

我的问题是使用 Marshal 注册远程对象。随着时间的推移,远程处理对象将消失以供客户端使用,即不再位于远程处理对象上。该服务仍将保留其参考。然后我尝试了 RegisterWellKnownServiceType 并且客户端不断获得正确的引用,但是我无法让服务引用同一个对象。

在这种情况下,解决方案是覆盖远程处理对象 FooRemoting。如果我覆盖 InitializeLifetimeService 并返回 null,则客户端将永远不会失去连接,而服务将保持连接。

public override object InitializeLifetimeService()
{
    //return base.InitializeLifetimeService();
    return null;
}

为了保留服务创建的对象并让客户端使用相同的对象,您必须使用

RemotingServices.Marshal(foo);

并覆盖 InitializeLifetimeService 以返回 null。

于 2009-01-29T21:49:26.727 回答
16

可以通过远程处理公开具有参数构造函数的 MarshalByRefObjects,并且该类的用户可以仅处理其接口。

我创建了一个小型概念验证项目。它有 3 个项目:服务器、客户端和核心。服务器和客户端都引用Core,但不相互引用。

在核心中,我们定义了一个服务接口:

namespace Core
{
    public interface ICountingService
    {
        int Increment();
    }
}

服务器定义了具体的实现,客户端没有引用

namespace Server
{
    public class CountingService : MarshalByRefObject, ICountingService
    {
        private static int _value = 0;

        public CountingService(int startValue)
        {
            _value = startValue;
        }

        public int Increment()
        { // not threadsafe!
            _value++;
            return _value;
        }
    }
}

需要注意的重要一点是它有一个带参数的构造函数,它是一个 MarshalByRefObject,它实现了核心项目中的接口。

服务器项目是一个控制台应用程序,它设置一个远程处理通道(在本示例中任意通过 HTTP),创建服务,并将其注册到远程处理:

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

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            HttpServerChannel serverChannel = new HttpServerChannel(8234);
            ChannelServices.RegisterChannel(serverChannel, false);

            // Following line won't work at runtime as there is no parameterless constructor
            //RemotingConfiguration.RegisterWellKnownServiceType(typeof(CountingService),
            //                     "CountingService.rem", WellKnownObjectMode.Singleton);

            CountingService countingService = new CountingService(5);
            RemotingServices.Marshal(countingService, "CountingService.rem");

            Console.WriteLine("Press enter to exit.");
            Console.ReadLine();
        }
    }
}

上面的代码已经注册了 URL http://localhost:8234/CountingService.rem,其中保存了实例化的服务,将从 5 开始计数。

客户端,也是一个控制台应用程序,然后可以使用接口类获取引用:

using System;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using Core;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            HttpClientChannel serverChannel = new HttpClientChannel();
            ChannelServices.RegisterChannel(serverChannel, false);

            for (int i = 0; i < 5; i++)
            {
                ICountingService countingService =
                    (ICountingService)Activator.GetObject(typeof(ICountingService),
                    "http://localhost:8234/CountingService.rem");

                int newValue = countingService.Increment();
                Console.WriteLine("Value is " + newValue);
            }

            Console.WriteLine("Press enter to exit.");
            Console.ReadLine();
        }
    }
}

当服务器和客户端运行时,它会打印从 6 到 10 的值。

总结:客户端只知道接口;实现构造函数可以有参数;实例化可以由您自己的代码而不是由 .NET 控制。在使用远程对象处理基于构造函数的依赖注入时非常有用。

于 2010-01-21T12:31:24.757 回答
1

我像这样用 RemotingServices.Marshal 做了一个实验

托管在 Windows Exe 中的远程组件。exe代码是

Form1_Load(object sender, EventArgs e)
{
   RemotingConfiguration.Configure("path of the config file");
   RemoteClass obj = new RemoteClass();
   obj.MyVal =100;

   RemotingServices.Marshal(obj);
}


public RemoteClass: MarshalByRefObj
{
   static int Counter;
   public RemoteClass()
   {
      Counter++;
   }

   int _MyVal =0;
  public int MyVal
 {
    get
   {
      return _MyVal;
   }
   set
   {
      _MyVal = value;
   }
 }       
}

现在在客户端代码中

button1_click()
{
  RemoteClass obj = Activator.GetObject(typeof(RemoteClass), "object URI");
  if(RemotingServices.IsTransparentProxy(obj))
  {
      MessageBox.Show(obj.Myval.ToString());
  }
}

它会弹出消息为 0 而不是 100。如果你在 RemoteClass 的构造函数中放置一个断点,你会看到构造函数被调用了 2 次

  1. 当在 Service 本身中创建 RemoteClass 对象时
  2. 当客户端调用 MyVal 属性时。

我认为 RemotingServices.Marshal 与单个实例无关。即使您仅使用 RemotingConfiguration.Configure 并覆盖 InitializeLifetimeService 以使其返回 null,也足以托管远程组件。

于 2009-07-01T03:07:06.287 回答