Introduction

.NET remoting is a beautiful framework, but its most serious deficiencies preventing it from being useful in today's Internet environment, is that it requires each node to have a global IP, so direct TCP connections can be made. Other incomplete solutions aimed at this problem exist (GenuineChannel), but they use special transport channels (instead of the standard TCP/HTTP), with large amount of unproven custom code, thus make your code heavier and unstable. These solutions also do not let 2 objects both behind firewall call each other, either.

Reachability channel sinks provide a more lightweight, elegant solution. Remoting objects hosted by machines with only local IPs behind firewalls can be made reachable from anywhere (including those machines behind firewalls themselves), using standard transport channels, by a Message Redirector. The whole process is transparent from upper, logic layers. Asynchronous calls, one-way calls are also supported.

Advantages of .NET remoting

.NET remoting has the the following advantages:

Separation of network-code from logic code.

Advantage over WebService: Clients can receive events from server. More types can be passed. Higher throughput.

Although .NET remoting is designed to accommodate the need of Enterprise intranet applications, nothing prevents it from being used over the Internet, at least not security problems - it is free from security holes - buffer overrun is impossible in managed code in which .NET remoting framework is entirely written. .NET remoting is ideal for peer-to-peer applications, because it alleviate programmers from writing complex network code that manages complex network topologies.

Code needed

Add reference of Reachability.dll to your project.

Hosts which need to expose objects via the Redirector must call Reachability.ServerSinkProvider.StartWaitRedirectedMsg() after RemotingConfiguration.Configure(), or after RemotingServices.Marshal(). The effect of this call is to start receiving messages from the Redirector.

How it works

There are already many articles on how .NET remoting works and on channel sinks, and I don't have time to repeat such descriptions here. The Reachability sinks have 3 components:

The Redirector service

The "Server" Channel Sink

The "Client" Channel Sink

Basically, server sink adds reachability information (i.e. via which Redirector can we send message to this object) to (ChannelData in ) the ObjRef. When this ObjRef is passed to somewhere, there, the client sink finds out that reachability information attached to the ObjRef, and instead of directly trying to connect to the object, it pass the call to the Redirector. The Redirector will deliver the call appropriately, provided the host which created the ObjRef is listening on the Redirector properly. (This is done by StartWaitRedirectedMsg()).

Here is the heart of the code that makes hosts behind firewall receive calls (without special transport channel):

The node behind firewall will not listen for incoming calls, instead it calls GetNextRequest() and blocks till an incoming call arrives (like the Windows API GetMessage()). The Redirector is blocked until ReturnResponse() is called.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Comments and Discussions

I've implemented your solution, and it works for 3 minutes, and then after that the client won't receive any calls from the server.
I'm running the server app on IIS, and using HTTP Channel & SOAP. Any ideas why this is happening?

I cannot believe that Microsoft Remoting will not work through NAT by default. What is the point of a remotable object infrastructure that does not just 'work' between objects that can obviously communicate with each other. Thanks for the code!

Big thanks for this great article, but now my question:
Is their a way to get the client to not open a listening socket, and still use this solution?

The problem i curently experience is that when i start the client it opens a listening socket which is noticed by the firewall, the firewall then pops-up and start asking if the Client should be allowed to be a server just to open a listening socket that is never used.

I could invoke remote methods from the server and get the result in my client application.
Now, I want to make server traitment (with many tasks in) that returns "endOfTask" events to the client when each task is coming over.

It works well when client is not behind a firewall but when client is behind a firewall, server returns a RemotingException !

hi, i am having problem that if client disconnected abnormally, then when any other client send message to that client, in that case its blocking the queue, not processing any request and server needs to be restart. so what can be done to solve this ? please reply

This looks to be a great solution. I'm currently transferring your configuration code into c# code. I noticed the sample was written to work with a configuration file. Perhaps at some point someone can upload a sample which does not use the configuration file.

First of all a big thanks to Zhiheng Cao, i have been looking for a solution like this for quite some time! THANKS for this great article.

For those who dont like the .config files here the code to do the same thing (i think).
If you use this code you can get rid of the server.exe.config and reduce client.exe.config to a single setting (or dont use that one either).
This way you can make all the settings from within the code, and the .config files are not needed to setup the connection.

Hope it helps someone.. Don't know how to test if everything with the redirecor still works, but client does seem to work OK from behind a firewall.

Hello,
I am trying to make your code work but without luck.
I don't understand how it's work because for example chat room it's not marshaled in order to became a proxy (all the clients must see this class to know the reigistred clients).
Cand you provide me the code with the configs files too?

Can you update your demo code to make it just build and run? It would be much better to make just two sulotions, one is server and one is client as well as those app.config files. Then I can just build them and run them. For now I cannot just download and run your demo. I have to do some work such as adding the config files. But I still cannot make them run correctly. I like your solution and it should be very useful to be used in many cases. But your description is very simple and I cannot really follow it. So if I have your demo run successfully, it will help me understand you solution. Thank you very much.

Could I ask you a small question?
With this structure, when installing client application, we must install the director application to another computer that is known by both of client computer and server computer, is that right?

Actually proposed solution is rather a big headache than a solution which will get you through firewalls/NAT/etc. You'll have to have redirecting sinks at all of your firewalls and so on. Instead of just implementing your solution using true bidirectional TCP channels (like Genuine Channels)

[Server ] --> [Firewall] --> [Client ]

You'll have to have either Server at the same computer where firewall is set up (Actually in this case this solution is pretty useless because actually no firewall between the server and the client, or/and you can set it up to allow incoming requests):

Again, instead of just creating your solution with bidirectional channels you will have to create several servers with redirecting sinks and you'll have to install them and tune them at each host with firewalls/NATs/etc.

Wanted to notice that with this solution
[Server ] --> [Firewall] --> [Client ]
you will only specify server's address while initializing client TCP channel. And it will work.

One thing you misunderstood is that you don't have to place Redirectors at your firewall, they can be placed anyplace to which the client behind NAT can make a connection. Since the Redirector does not call back the client, the client does not have to be connectable from Redirector.
Suppose you plan to use this solution to provide a service. Users behind NAT will only have to install your client in their machine. The client can be configured to connect to a certain address, can call GetNextRequest() and wait. This way the client behind NAT can receive requests.

So we want to reach the client from our server:
server [with sink] --> redirector server [with sink, it's OK] -- Oops. We can't get though the NAT because client is in LAN and is unreachable (Microsoft channels have to open direct connection).
Your solution won't provide back access. Any bidirectional channel will do.

In fact, the "bidirectional" channels such as GenuineChannel work because it sends message back to client behind NAT using the same socket that it received from the "accept" socket call. This is the way web servers send back response to clients behind firewall. What NAT prevents, is that the server call the "connect" socket call, to try to actively connect to the client behind NAT.
My solution(Redirector) works the same way. The Redirector never call "connect" to actively send messages to client behind NAT. This is because it does not need to "unmarshal" MBR objects in client. Instead, the client calls the method of Redirector, "GetNextRequest". This method blocks until the Redirector receives request for that client. Then the "GetNextRequest" returns, along with the request stream in a byte[] block.
Did this explanation cleared your doubt?

P.S.
One possible drawback of this solution is that the Redirector must hold a thread in the thread pool for each client that is being blocked by GetNextRequest. You will need to enlarge the thread pool count otherwise it can only serve 25 clients.