.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()
。