在使用 .NET Remoting 开发跨进程应用的时候,你可能会遇到一些异常。因为这些异常在后验的时候非常简单但在一开始有各种异常烦扰的时候却并不清晰,所以我将这些异常整理到此文中,方便小伙伴们通过搜索引擎查阅。


连接到 IPC 端口失败: 系统找不到指定的文件

System.Runtime.Remoting.RemotingException:“连接到 IPC 端口失败: 系统找不到指定的文件。”

或者英文版:

System.Runtime.Remoting.RemotingException: Failed to connect to an IPC Port: The system cannot find the file specified.

出现此异常时,说明你获取到了一个远端对象,但是在使用此对象的时候,甚至还没有注册 IPC 端口。

比如,下面的代码是注册一个 IPC 端口的一种比较粗暴的写法,传入的 portName 是 IPC 的 Uri 路径前缀。例如我可以传入 walterlv,这样一个 IPC 对象的格式大约类似 ipc://walterlv/xxx

private static void RegisterChannel(string portName)
{
    var serverProvider = new BinaryServerFormatterSinkProvider
    {
        TypeFilterLevel = TypeFilterLevel.Full,
    };
    var clientProvider = new BinaryClientFormatterSinkProvider();
    var properties = new Hashtable
    {
        ["portName"] = portName
    };
    var channel = new IpcChannel(properties, clientProvider, serverProvider);
    ChannelServices.RegisterChannel(channel, false);
}

当试图访问 ipc://walterlv/foo 对象并调用其中的方法的时候,如果连 walterlv 端口都没有注册,就会出现 连接到 IPC 端口失败: 系统找不到指定的文件。 异常。

如果你已经注册了 walterlv 端口,但是没有 foo 对象,则会出现另一个错误 找不到请求的服务,请看下一节。

找不到请求的服务

System.Runtime.Remoting.RemotingException:“找不到请求的服务”

或者英文版:

System.Runtime.Remoting.RemotingException: Requested Service not found

当出现此异常时,可能的原因有三个:

  1. 要查找的远端对象尚未创建;
  2. 要查找的远端对象已被回收;
  3. 没有使用匹配的方法创建和访问对象。

更具体来说,对于第一种情况,就是当你试图跨进程访问某对象的时候,此对象还没有创建。你需要做的,是控制好对象创建的时机,创建对象的进程需要比访问它的进程更早完成对象的创建和封送。也就是下面的代码需要先调用。

RemotingServices.Marshal(@object, typeof(TObject).Name, typeof(TObject));

而对于第二种情况,你可能需要手动处理好封送对象的生命周期。重写 InitializeLifetimeService 方法并返回 null 是一个很偷懒却有效的方法。

namespace Walterlv.Remoting.Framework
{
    public abstract class RemoteObject : MarshalByRefObject
    {
        public sealed override object InitializeLifetimeService() => null;
    }
}

而对于第三种情况,你需要检查你是如何注册 .NET Remoting 通道的,创建和访问方式必须匹配。

信道“ipc”已注册

System.Runtime.Remoting.RemotingException:“信道“ipc”已注册。”

在同一个进程中,IpcChannel 类的默认信道名称 IpcChannel.ChannelName 值是字符串 "ipc"。如果你不通过它的参数 properties 来指定 ["name"] = "另一个名称",那么你就不能重复调用 ChannelServices.RegisterChannel 来调用这个信道。

说简单点,就是上面的方法 RegisterChannel 你不能在一个进程中调用两次,即便 "portName" 不同也不行。通常你也不需要去调用两次,如果一定要,请通过 HashTable 修改 name 属性。


参考资料


本文会经常更新,请阅读原文: https://dotnet-campus.github.io//post/dotnet-remoting-exceptions.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 dotnet 职业技术学院 (包含链接: https://dotnet-campus.github.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系