C# 在.NET远程处理中,RemotingConfiguration.RegisterWellKnown服务类型和RemotingServices.Marshal之间有什么区别?

C# 在.NET远程处理中,RemotingConfiguration.RegisterWellKnown服务类型和RemotingServices.Marshal之间有什么区别?,c#,.net,remoting,C#,.net,Remoting,在.NET远程处理中,RemotingConfiguration.RegisterWellKnown服务类型和RemotingServices.Marshal之间有什么区别 我要做的是在Windows服务中创建一个对象,然后将其作为远程处理对象放入,并让Windows服务和客户端都对远程处理对象进行操作 我认为下面的代码可以实现这一点 FooRemoting foo = new FooRemoting(); RemotingConfiguration.RegisterWellKnownServ

在.NET远程处理中,RemotingConfiguration.RegisterWellKnown服务类型和RemotingServices.Marshal之间有什么区别

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

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

FooRemoting foo = new FooRemoting();

RemotingConfiguration.RegisterWellKnownServiceType(typeof(FooRemoting), serverName, WellKnownObjectMode.Singleton);
RemotingServices.Marshal(foo);
这就是我发现的

RemotingConfiguration.RegisterWellKnownServiceType(typeof(FooRemoting), 
          serverName, WellKnownObjectMode.Singleton);
RegisterWellKnownServiceType将创建该对象,并使其成为使用该对象的任何客户端的单例,但不会创建服务器的引用。只有在客户机请求时才会创建该对象,并且同一对象用于任何其他客户机

RemotingServices.Marshal(foo);
封送处理将注册服务器创建的对象,在本例中为windows服务。然后,服务器将引用该对象,客户端将使用相同的对象

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

在本例中,解决方案将覆盖远程处理对象FooRemoting。如果我重写InitializeLifetimeService并返回null,客户端将永远不会失去连接,服务将, 保持联系

public override object InitializeLifetimeService()
{
    //return base.InitializeLifetimeService();
    return null;
}
为了保留服务创建的对象,并让客户端使用您必须使用的同一对象

RemotingServices.Marshal(foo);

并重写InitializeLifetimeService以返回null。

我用RemotingServices做了一个实验

托管在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次

  • 在服务本身中创建RemoteClass对象时
  • 当客户端调用MyVal属性时

  • 我认为RemotingServices.Marshal与单个实例无关。即使只使用远程配置。配置并重写InitializeLifetimeService,使其返回null,就足以承载远程组件。

    可以通过远程公开具有参数构造函数的MarshalByRefObject,类的用户也可以只处理它的接口

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

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

    namespace Core
    {
        public interface ICountingService
        {
            int Increment();
        }
    }
    
    服务器定义了具体的实现,客户端没有对其进行引用:

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

    服务器项目是一个控制台应用程序,它设置远程通道(在本例中可通过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,它将从5开始计数

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

    使用系统;
    使用System.Runtime.Remoting.Channels;
    使用System.Runtime.Remoting.Channels.Http;
    使用核心;
    命名空间客户端
    {
    班级计划
    {
    静态void Main(字符串[]参数)
    {
    HttpClientChannel服务器通道=新的HttpClientChannel();
    ChannelServices.RegisterChannel(serverChannel,false);
    对于(int i=0;i<5;i++)
    {
    i计算服务计数服务=
    (ICountingService)Activator.GetObject(类型为(ICountingService),
    "http://localhost:8234/CountingService.rem");
    int newValue=countingService.Increment();
    Console.WriteLine(“值为”+newValue);
    }
    控制台。WriteLine(“按enter键退出”);
    Console.ReadLine();
    }
    }
    }
    
    当服务器和客户端运行时,它会打印6到10之间的值


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

    感谢您将整个示例汇集在一起。我已经有很长一段时间没有远程处理了,这篇帖子对我来说是一个很好的“即时提醒”。谢谢你,伙计!完美答案。这正是我所缺少的。不考虑引用问题,我注意到这里的另一个区别是在使用
    远程服务时。封送
    ,对其属性的所有更改、事件注册都会得到维护(这意味着来自客户端的任何调用都可能引发服务对象上的某些事件,…)但是当使用RegisterWellKnownServiceType时,我没有任何方法来注册某些特定事件或更改所创建对象的某些特定属性(我们甚至无法访问所创建的对象?),这意味着一切都应该在对象类内完成。
    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();
            }
        }
    }
    
    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();
            }
        }
    }