【问题标题】:.net remoting, delegate getting called in wrong process.net 远程处理,委托在错误的进程中被调用
【发布时间】:2015-09-27 06:03:54
【问题描述】:

我正在用 .net remoting 做一些测试,发现在使用委托时它有问题。

我有一个既是服务器又是客户端的应用程序。当用户第一次从资源管理器运行应用程序时,它作为服务器运行并作为客户端启动一个新进程。两者都工作正常。现在,当用户在服务器和客户端进程仍在运行时再次运行它时,它假设成为客户端并向服务器发送有关新进程启动的消息,然后自行终止。

除了委托在服务器进程中执行之外,一切正常 客户。

这里是代码。

    const string PIPE_NAME = "testPipeName33";
    const string OBJECT_NAME = "test";
    static RemoteObject remoteObject;
    static void RegisterClient()
    {
        IpcClientChannel chan = new IpcClientChannel();
        ChannelServices.RegisterChannel(chan, false);

        remoteObject = (RemoteObject)Activator.GetObject(typeof(RemoteObject),
                string.Format("ipc://{0}/{1}", PIPE_NAME, OBJECT_NAME));
    }
    static void RegisterServer()
    {
        BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
        serverProvider.TypeFilterLevel = TypeFilterLevel.Full;

        IpcServerChannel chan = new IpcServerChannel("", PIPE_NAME, serverProvider);
        ChannelServices.RegisterChannel(chan, false);

        RemotingServices.Marshal(new RemoteObject(), OBJECT_NAME);
    }

    [STAThread]
    static void Main(string[] args)
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        if ((args.Length == 0 || args[0] == "s"))
        {
            try
            {
                RegisterServer();
            }
            catch (RemotingException)
            {
                // try to register it with the pipe name. If it fails, means server is already running.
                //bad idea, I know, but it's just for barebone quick test
                RegisterClient();
                remoteObject.OnNewProcessStarted("test");
                Application.Exit();
                return;
            }

            Process.Start(Application.ExecutablePath, "c");
            Application.Run(new Form1("Server"));

        }
        else
        {
            IsClient = true;
            RegisterClient();
            remoteObject.SetOnNewProcessStarted(OnNewProcessStarted);

            Application.Run(new Form1("Client"));

        }
    }


    static bool IsClient = false;
    static bool OnNewProcessStarted(string commandLine)
    {
        MessageBox.Show("Is Client : " + IsClient);//problem here, IsClient should be true
        return true;
    }

RemoteObject 类。

public delegate bool OnNewProcessStartedDelegate(string text);

internal class RemoteObject : MarshalByRefObject
{
    public OnNewProcessStartedDelegate OnNewProcessStartedHandler;
    public bool OnNewProcessStarted(string commandLine)
    {
        if (OnNewProcessStartedHandler != null)
            return OnNewProcessStartedHandler(commandLine);
        return false;
    }

    public void SetOnNewProcessStarted(OnNewProcessStartedDelegate onNewProcessStarted)
    {
        OnNewProcessStartedHandler = onNewProcessStarted;
    }

    public override object InitializeLifetimeService()
    {
        return null;
    }
}

PS : 只能有一台服务器和一台客户端。

【问题讨论】:

  • 这种情况正确吗? "如果 ((args.Length == 0 || args[0] == "s"))"
  • 条件表示如果没有参数或者如果第一个参数是s那么它将执行服务器。您是否验证了参数不止一个且第一个参数不是“s”?
  • 是的,参数和条件都是正确的。
  • 您能否发布第二次调用应用程序时传递的参数是什么?
  • 全部代码已经贴出来了,大家看看吧。

标签: c# .net .net-remoting


【解决方案1】:

Delegate 正在服务器上运行,因为新客户端正在服务器上调用 RemoteObject 的委托。服务器和第一个客户端之间没有注册回调。服务器将不知道要回调哪个进程,因为第一个客户端侦听请求中没有人。 通常使用 WCF 之类的技术,我们可以使用 DualHttpBindings,它允许双工通信,但在这里我们没有任何回调。所以我们必须在第一个客户端注册一个带有 RemoteObject 的回调通道,这将确保在第一个客户端进程中调用委托。我已经尝试过它并且它正在工作。我添加了一些用于调试目的的进程 ID。

  static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            const string PIPE_NAME = "testPipeName33";
            const string OBJECT_NAME = "test";
            const string CALLBACK_PIPE_NAME = "testPipeName34";
            const string CALLBACK_OBJECT_NAME = "testclient";

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            if ((args.Length == 0 || args[0] == "s"))
            {
                try
                {
                   IPCRegistration.RegisterServer(PIPE_NAME,OBJECT_NAME);
                }
                catch (RemotingException)
                {
                                        remoteObject = IPCRegistration.RegisterClient(typeof(RemoteObject),PIPE_NAME,OBJECT_NAME);
                    remoteObject.OnNewProcessStarted("test");
                    Application.Exit();
                    return;
                }
                MessageBox.Show("Server:" + Process.GetCurrentProcess().Id);
                Process.Start(Application.ExecutablePath, "c");
                Application.Run(new Form1("Server"));

            }
            else
            {
                IsClient = true;
                remoteObject = IPCRegistration.RegisterClient(typeof(RemoteObject), PIPE_NAME, OBJECT_NAME);
                IPCRegistration.RegisterServer(CALLBACK_PIPE_NAME, CALLBACK_OBJECT_NAME); // Here Client will listen on this channel.
                remoteObject.SetOnNewProcessStarted(OnNewProcessStarted,Process.GetCurrentProcess().Id.ToString());
                MessageBox.Show("Client:" + Process.GetCurrentProcess().Id);
                Application.Run(new Form1("Client"));

            }
        }


        static RemoteObject remoteObject;

        static bool IsClient = false;
        static bool OnNewProcessStarted(string commandLine)
        {
            MessageBox.Show("saved:"+commandLine+" Currrent:"+Process.GetCurrentProcess().Id);
            MessageBox.Show("Is Client : " + IsClient);//problem here, IsClient should be true
            return true;
        }
    }

    public delegate bool OnNewProcessStartedDelegate(string text);

    internal class RemoteObject : MarshalByRefObject
    {
        public OnNewProcessStartedDelegate OnNewProcessStartedHandler;
        public string value;
        public bool isCallBack = false;
        const string PIPE_NAME = "testPipeName33";
        const string OBJECT_NAME = "test";
        const string CALLBACK_PIPE_NAME = "testPipeName34";
        const string CALLBACK_OBJECT_NAME = "testclient";
        RemoteObject remoteObject;
        public bool OnNewProcessStarted(string commandLine)
        {
            if (!isCallBack)
            {
                remoteObject.isCallBack = true;
                return remoteObject.OnNewProcessStarted(commandLine);
            }

            if (OnNewProcessStartedHandler != null)
                return OnNewProcessStartedHandler(value);
            return false;
        }



        public void SetOnNewProcessStarted(OnNewProcessStartedDelegate onNewProcessStarted,string value)
        {
            this.value = value;
            OnNewProcessStartedHandler = onNewProcessStarted;
            if (!isCallBack)
            {
                remoteObject = IPCRegistration.RegisterClient(typeof(RemoteObject), CALLBACK_PIPE_NAME, CALLBACK_OBJECT_NAME);
                remoteObject.isCallBack = true;
                remoteObject.SetOnNewProcessStarted(onNewProcessStarted, Process.GetCurrentProcess().Id.ToString());
            }
        }

        public override object InitializeLifetimeService()
        {
            return null;
        }
    }

    internal class IPCRegistration
    {
       public  static RemoteObject RegisterClient(Type remoteObject,string PIPE_NAME,string OBJECT_NAME)
        {
            IpcClientChannel chan = new IpcClientChannel();
            ChannelServices.RegisterChannel(chan, false);

            RemoteObject remoteObjectInstance = (RemoteObject)Activator.GetObject(remoteObject,
                    string.Format("ipc://{0}/{1}", PIPE_NAME, OBJECT_NAME));
            return remoteObjectInstance;
        }
        public static void RegisterServer(string pipeName, string objectName)
        {
            BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
            serverProvider.TypeFilterLevel = TypeFilterLevel.Full;

            IpcServerChannel chan = new IpcServerChannel("", pipeName, serverProvider);
            ChannelServices.RegisterChannel(chan, false);

            RemotingServices.Marshal(new RemoteObject(), objectName);
        }
    }

【讨论】:

  • 这是不正确的。为此进程设置 IsClient 为 true 是不正确的。因为当 OnNewProcessStarted() 将被调用时,它将触发假定在服务器进程之后创建的第一个客户端中执行的委托。这是正在发生的事情,当应用程序第一次运行时,它作为服务器运行并且还为客户端启动一个新进程。现在,如果用户将运行任何其他进程,它将成为远程对象上的客户端调用 OnNewProcessStarted 并杀死自己。当服务器和第一个客户端保持运行时。我希望我现在清楚了。
  • “它将成为远程对象上的客户端调用 OnNewProcessStarted 并杀死自己”这里你正在做的是通过调用 RegisterClient 然后调用 OnNewProcessStartedMethod 获取 Remoteobject 但你的 isClient 是静态变量,运行时将为 false程序再次..您尚未将值设置为 true,因此当委托被触发时 isClient 显示为 false
  • 为什么要第二次调用 OnNewProcessStart 事件?我看到您在那之后立即退出应用程序...为什么不直接退出应用程序而不调用 OnNewProcessStart?
  • 您似乎误解了应用程序的结构。在这里我再试一次。用户第一次运行应用程序,应用程序成为服务器并作为其客户端启动一个新进程。客户端和服务器都保持运行。但是现在,用户再次运行该应用程序,现在因为它不能成为服务器,因为已经有一个服务器在运行。所以它只是成为客户端并向服务器发送消息并死掉。然后服务器将该消息传输到它启动的客户端。它的“客户端到服务器到客户端”消息。但这并没有发生,它变成了“客户端到服务器到服务器”的消息。
  • 我相信您误解了委托的概念...当您第二次运行您的应用程序时,它不会在第一个客户端进程下调用委托方法,而是会在新的客户端进程中调用....尝试这是第一次运行您的应用程序..服务器和客户端都将运行,然后关闭客户端并再次运行该应用程序现在无法正确调用第一个客户端的委托,但您仍然会看到消息框....您可以添加更多消息框中的信息,以了解它处于哪个进程才能完全理解。我猜代码只能正常工作..它没有在服务器下运行...
【解决方案2】:

在应用程序的第二次运行中,您没有在 RemoteObject 中设置委托,因此使用之前设置的委托对象(它是在您的应用程序的第一次运行中设置的)。

我认为,如果您使用“c”之类的命令参数运行第二个应用程序,则委托将在客户端进程中运行。

【讨论】:

  • 我不明白你的回答,委托应该在客户端进程中执行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多