2011-06-12 115 views
2

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

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

所以,我的问题是:我可以解决这个问题,所以它不会复制AppDomain之间的这么多数据?或者有没有更好的方法来做到这一点,不使用MarshalByRefObject?

+2

编组套接字而不是数据。不知道该怎么做,SocketInformation看起来很诱人。 – 2011-06-12 15:06:48

+0

这工作:)我曾想过,但认为没有办法序列化套接字(技术上仍然是真的)。我只是使用Socket.DuplicateAndClose获取SocketInformation(它是可序列化的)并使用它在新的AppDomain中创建套接字。谢谢。 – toby 2011-06-12 20:55:43

+1

帮我们一个忙,写一个答案。 AndClose让我感到困惑。 – 2011-06-12 21:20:12

回答

2

我最终将SocketInformation传递给新的AppDomain,并在那里创建一个新的套接字对象(在新的AppDomain中)。

在我的“沙盒包装对象,”我有这样的功能:

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对象发生了什么细节,但它运作良好至今。

3

字符串是按进程而不是按应用程序域(出于性能原因)。如果传递html/xml(而不是二进制数据),则可以将您的api更改为使用Strings而不是byte []。您甚至可以在常见情况下支持字符串,在二进制情况下支持byte []。

+1

好主意,我不知道字符串是以这种方式处理的,并且大部分时间都是字符串数据,尽管我仍然需要支持二进制数据,如果这样做效率很高,它也会很好。谢谢回复! – toby 2011-06-12 14:37:32

+2

这样的特殊索赔需要非同寻常的证据。也许这对interned字符串是真的,如果,OP肯定不会处理interned字符串。 – 2011-06-12 14:57:10

+0

您的套接字库是否允许您从另一个AppDomain写入套接字? – 2011-06-12 14:58:02

1

如果这是.NET 4.0将使用Memory mapped file是一个选项?

+0

我正在使用.NET 4.0,你的意思是从一个AppDomain写入内存映射文件并从另一个AppDomain读取它? – toby 2011-06-12 23:48:37

+0

是的,专门针对较大的文件。虽然5mb可能不够大,您可以看到我认为可能值得考虑的好处。 – 2011-06-13 08:17:53