.NET Remoting 消息重定向通道接收器






3.38/5 (20投票s)
2003年6月21日
3分钟阅读

117857

1676
一种上层逻辑层透明的方式来重定向 .NET remoting 调用,从而使 .NET remoting 服务能够穿透防火墙/NAT,到达任何地方。
引言
.NET remoting 是一个很棒的框架,但它最严重的缺陷在于阻止其在当今互联网环境中的应用,因为它要求每个节点都拥有一个全局 IP,以便能够建立直接的 TCP 连接。 存在其他针对此问题的未完成解决方案 (GenuineChannel),但它们使用了特殊的传输通道(而不是标准的 TCP/HTTP),其中包含大量未经检验的自定义代码,从而使您的代码变得更重且不稳定。 这些解决方案也不允许两个位于防火墙后的对象互相调用。
Reachability 通道接收器提供了一种更轻量、优雅的解决方案。 通过 *消息重定向器*,可以使用标准传输通道,使得位于防火墙后的仅具有本地 IP 的机器所托管的 remoting 对象可以从任何地方访问(包括这些机器本身位于防火墙之后)。 整个过程对上层逻辑层是透明的。 也支持异步调用和单向调用。
.NET remoting 的优势
.NET remoting 具有以下优势
- 将网络代码与逻辑代码分离。
- 优于 WebService:客户端可以从服务器接收事件。可以传递更多类型。更高的吞吐量。
虽然 .NET remoting 旨在满足企业内部网应用程序的需求,但没有任何东西阻止它在 Internet 上使用,至少没有安全问题——它没有安全漏洞——缓冲区溢出在完全用 .NET remoting 框架编写的托管代码中是不可能的。.NET remoting 非常适合点对点应用程序,因为它减轻了程序员编写管理复杂网络拓扑的复杂网络代码的工作。
如何在您的项目中进行使用
配置文件
- 对于位于防火墙后面并且需要公开主机的对象<appSettings> <add key="RedirectorURL" value="tcp://Redirector's IP:Port/Redirector.rem" /> </appSettings> .... <serverProviders> <provider type="Reachability.ServerSinkProvider, Reachability" /> <formatter ref="binary" /> </serverProviders>
- 对于需要通过 Redirector调用对象的宿主<clientProviders> <formatter ref="binary" /> <provider type="Reachability.ClientSinkProvider, Reachability" /> </clientProviders>
- 对于 Redirector<wellknown mode="Singleton" type="Reachability.Redirector, Reachability" objectUri="Redirector.rem" /> ... <channels> <channel ref="tcp" port="Redirector's Port" > <serverProviders> <formatter ref="binary" /> </serverProviders> <clientProviders> <formatter ref="binary" /> </clientProviders> </channel> </channels>
所需的代码
将 Reachability.dll 的引用添加到您的项目中。
需要通过 Redirector 公开对象的宿主必须在 RemotingConfiguration.Configure() 之后或 RemotingServices.Marshal() 之后调用 Reachability.ServerSinkProvider.StartWaitRedirectedMsg()。 此调用的效果是开始从 Redirector 接收消息。
工作原理
已经有很多关于 .NET remoting 工作原理和通道接收器的文章,我没有时间在这里重复这些描述。 Reachability 接收器有 3 个组件
- Redirector服务
- “服务器”通道接收器
- “客户端”通道接收器
基本上,服务器接收器将可达性信息(即,我们可以通过哪个 Redirector 将消息发送到此对象)添加到(ChannelData 中的)ObjRef。 当此 ObjRef 传递到某处时,客户端接收器发现附加到 ObjRef 的可达性信息,并且它不直接尝试连接到该对象,而是将调用传递给 Redirector。 只要创建 ObjRef 的宿主在 Redirector 上正确侦听,Redirector 就会适当地传递调用。(这是通过 StartWaitRedirectedMsg() 完成的)。
这是使防火墙后面的宿主接收调用的代码的核心(无需特殊传输通道)
public void RedirectRequest(Guid slot, bool oneway, 
     ITransportHeaders requestHeaders, byte[] requestStream, 
     out ITransportHeaders responseHeaders, out byte[] responseStream)
{
   SyncQueue q = reqs[slot] as SyncQueue;  
   if(q==null)reqs.Add(slot, q=new SyncQueue());
   q.Enqueue(new Message(requestHeaders, 
       requestStream, Thread.CurrentPrincipal, oneway));
   
   if(!oneway)
   {
      q = resps[slot] as SyncQueue;   
      if(q==null)resps.Add(slot, q=new SyncQueue()); 
      Response r = q.Dequeue() as Response;
      responseHeaders = r.responseHeaders;
      responseStream = r.responseStream;
   }
   else
   {
      responseHeaders = null;
      responseStream = null;
   }
}
.....
   static void WaitForRequest(object o)
   {
      Redirector r = Activator.GetObject(typeof(Reachability.Redirector), 
                     RedirectorUrl) as Reachability.Redirector;
      Redirector.Message m;
      try
      {
         while(true)
         {
            r.GetNextRequest(rdata.Slot, out m);
            if(m==null)break;
          // must place this sink after formatter before
          // server transport, so message goes into stream.
            IMessage respMsg;    
                ITransportHeaders respHeader;
                Stream respStream;
            ServerChannelSinkStack stack = 
                  new ServerChannelSinkStack();     
                stack.Push(new ServerSink(theSink),r);
     
               theSink.ProcessMessage(stack,null,
                 m.requestHeaders,
                 new MemoryStream(m.requestStream),
               out respMsg, out respHeader, out respStream);
               if(!m.oneway)
               {
                    r.ReturnResponse(rdata.Slot, 
              new Redirector.Response(respHeader, respStream));
           }
        }
     }
     catch(Exception e){
        System.Diagnostics.Trace.Write(e.ToString());
     }
   }
防火墙后面的节点不会侦听传入的调用,而是调用 GetNextRequest() 并阻塞,直到传入的调用到达(类似于 Windows API GetMessage())。 Redirector 将被阻塞,直到调用 ReturnResponse()。
