C# C:沙箱和性能(MarshallByRefObject)

C# C:沙箱和性能(MarshallByRefObject),c#,.net,sandbox,C#,.net,Sandbox,我已经用C编写了一个非常基本的web服务器,它加载自定义模块,处理对特定域名的请求,如配置文件中所指定的。自定义模块被加载到新的AppDomain中,因为我需要动态卸载它们的能力,这也有利于安全性。因为模块加载到新的AppDomain中,所以所有参数和返回类型都是MarshalByRefObject。这很好,我传递了一个从MarshallByRefObject继承的HttpRequest对象,并返回一个LinkedList,该LinkedList由web服务器发送回客户端 所有这些都可以正常工作

我已经用C编写了一个非常基本的web服务器,它加载自定义模块,处理对特定域名的请求,如配置文件中所指定的。自定义模块被加载到新的AppDomain中,因为我需要动态卸载它们的能力,这也有利于安全性。因为模块加载到新的AppDomain中,所以所有参数和返回类型都是MarshalByRefObject。这很好,我传递了一个从MarshallByRefObject继承的HttpRequest对象,并返回一个LinkedList,该LinkedList由web服务器发送回客户端

所有这些都可以正常工作,但很多数据都是以字节[]的形式传递的,我相信MarshalByRefObject的代理将把所有字节从新的AppDomain复制到主AppDomain,而不是直接访问它们。因此,如果我说的没错,如果其中一个模块将发送一个5MB文件作为响应,那么5MB将在模块中加载/生成,然后从模块AppDomain复制到主AppDomain,最后通过套接字发送回客户端


所以,我的问题是:我能不能绕过这个问题,这样它就不会在AppDomain之间复制太多数据?或者有更好的方法不使用MarshallByRefObject吗?

出于性能原因,字符串是每个进程而不是每个appdomain。如果传递的是html/xml而不是二进制数据,则可以将api更改为使用字符串而不是字节[]。您甚至可以在普通情况下支持字符串,在二进制情况下支持字节[]。

我最终将SocketInformation传递给了新的AppDomain,并在新的AppDomain中创建了一个新的socket对象

在我的沙盒包装器对象中,我有以下函数:

internal class Sandbox
{
    private AppDomain _AppDomain;
    private WebApplicationProxy _Proxy;

    public Sandbox(string assemblyFile)
    {
        _AppDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString());
        _Proxy = (WebApplicationProxy)_AppDomain.CreateInstanceAndUnwrap(
                                                    Assembly.GetExecutingAssembly().FullName,
                                                    "WebServer.WebApplication.Proxy");
        _Proxy.Initialize(assemblyFile);
    }
    public void SendResponse(Socket client, HttpRequest request)
    {
        SocketInformation clientInfo = client.DuplicateAndClose(Process.GetCurrentProcess().Id);
        _Proxy.GetResponse(clientInfo, request);
    }
}
在新的AppDomain恢复套接字时,我还不确定幕后到底发生了什么。。。代码如下:

internal class Proxy : MarshalByRefObject
{
    private AppController _AppController;

    public void Initialize(string assemblyFile)
    {
        Assembly asm = Assembly.LoadFile(assemblyFile);
        var q = from t in asm.GetTypes()
                where t.GetInterfaces().Contains(typeof(AppController))
                      && !t.IsAbstract && t.IsClass
                select t;
        foreach (Type t in q)
        {
            _AppController = (AppController)Activator.CreateInstance(t);
        }
    }

    public void SendResponse(SocketInformation clientInfo, HttpRequest req)
    {
        Socket client = new Socket(clientInfo);

        LinkedList<byte[]> toSend = _AppController.GetResponse(client, req);

        foreach (byte[] bytes in toSend)
            client.Send(bytes);

        client.Close();
    }
}

我不确定调用DuplicateAndClose或在新域中创建新的Socket对象时会发生什么,但到目前为止,它工作得很好。

如果是.NET 4.0,使用a会是一个选项吗?

好主意,我不知道字符串是这样处理的,大多数时候它都是字符串数据,虽然我仍然需要支持二进制数据,如果这也是有效的,那就太好了。谢谢你的回复!像这样非同寻常的主张需要非同寻常的证据。也许对于内部字符串是这样的,大的如果,OP肯定不会处理内部字符串。你的套接字库允许你从不同的AppDomain写入套接字吗?@Hans Passant。说得好。我面前没有Jeffrey Richter的书,但我认为实现者决定,既然字符串应该是不可变的,就没有令人信服的理由在同一个过程中复制AppDomains之间的字符串。538,当跨AppDomain边界封送字符串对象时,CLR仅通过边界传递对字符串对象的引用;它不会复制字符串对象。CLR可以提供这种优化,因为字符串对象是不可变的,因此,一个AppDomain中的代码不可能损坏字符串对象的字符。封送套接字而不是数据。不知道如何做到这一点,SocketFormation看起来很诱人。这是可行的:我已经想到了这一点,但我认为没有办法序列化socket,从技术上讲仍然是正确的。我只是使用Socket.DuplicateAndClose来获取可序列化的SocketInformation,并使用它在新的AppDomain中创建套接字。谢谢。帮个忙,写个答案。AndClose让我很困惑。我使用的是.NET 4.0,你的意思是从一个AppDomain写入内存映射文件,然后从另一个AppDomain读取它吗?是的,专门针对较大的文件。虽然5mb的容量可能不足以让您看到好处,但我认为它可能值得考虑。