NATs (Network Address Translation) prevents the private IP address of your home and/or work PC to be exposed to the big wild and bad Internet. The only thing nodes outside of the NAT-shielded network can see is the IP address of the NAT-capable router device. A firewall should prevent unwanted network traffic from both inside and outside of a network - and personal firewalls even scale this wish down to the single computer, again at work or at home.
Cool. This is good and surely not extremely latest news for you. But this
fact and these two measurements makes building certain kinds of distributed
systems a little bit... well, tough, to say the least. Or as I would put it in
my words: "It totally sucks to build duplex communication over the Internet as
we all want to be safe and secure.".
I have been facing the wish to call from an Internet-located resource to an
enterprise- or home-located program in a secure fashion countless times. There
are tons of programs that can do this, like MSN Messenger or Skype, to name just
a few very prominent ones. So, what can we do when we want to build a
distributed solution based on Windows, based on .NET, based on WCF? Surely, we
could build everything that Messenger et. al. does on our own or just hijack
Messenger. But I think there should be some more general purpose approach which
is potentially highly interoperable (more
to come...).
WCF has this neat feature of duplex channels and callback contracts. You can do duplex messaging in WCF over different transports, like TCP, Named Pipes and even HTTP. In the latter case you need to set up (or better: let WCF do it for you) an HTTP-based callback endpoint. This is either done automatically for you (watch out for some interesting security side effect or you can decide which address your consuming application should listen on. Whatever approach you use, you just cannot beat firewalls and you surely lose the battle against NATs - so what to do?
We need some helpers, as always.
This helper shows up in the shape of the
Connectivity and the Identity
services from the BizTalk Services labs
cornucopia. We can use the Connectivity Service as a relay in order to enable
the above described callback scenario. There are already some very good
philosophical and
architectural
postings and
descriptions of the currently available services.
For duplex communication to happen, the key secret is that we do not only register the service with the relay but also the client's base callback address. Of course we need to model the service and consumer conversation and we decide to go for WCF's opt-in callback contract model. Next we add a duplex-capable binding and should be ready to go.
First, let's have a look at the major ServiceContract for a really simple example. We just want to be able to subscribe and unsubscribe to a service to get notifications when certain topics occur. Yeah, I know, this is far from being a complete and production-ready pub/sub framework - if you look for this please switch over to my friend Juval.
[ServiceContract(
Name = "PubSub",
Namespace = "http://thinktecture.com/services/relay/pubsub",
ConfigurationName="PSC",
CallbackContract=typeof(IPubSubCallback))]
public interface IPubSub
{
[OperationContract(IsOneWay=true)]
void Subscribe(string subMessage);
[OperationContract(IsOneWay = true)]
void Unsubscribe(string unsubMessage);
}
For sake of completeness, this is the CallbackContract to publish data from the service to the consumers.
[ServiceContract(
Name = "PubSubCallback",
Namespace = "http://thinktecture.com/services/relay/pubsub",
ConfigurationName="PSCC")]
public interface IPubSubCallback
{
[OperationContract(IsOneWay = true)]
void Publish(string pubMessage);
}
So far, nothing really new and extraordinary exciting, at least if you are familiar with basic duplex callbback contract modeling.
Second, we now need a binding that a) supports duplex communication and b) of
course can do the relaying from the BizTalk Labs Connectivity service. That
means that netTcpBinding,
netNamedPipeBinding and
wsDualHttpBinding will not work - for obvious reasons.
Following is the service-side config for our simple duplex relay sample. This
shows a little known but incredibly powerful way to make up a custom binding
with duplex capability. We use the relayTransport
from the BizTalk Services SDK and stack on top if it a binary message encoder.
Then we need a oneWay element in order to be
able to take a IDuplexSessionChannel or a
IRequestChannel and expose it as a
IOutputChannel, or conversely it can take a
IDuplexSessionChannel or a
IReplyChannel and expose it as a
IInputChannel. Last but not least we add a
compositeDuplex element.
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="duplexRelay">
<compositeDuplex />
<oneWay />
<binaryMessageEncoding />
<relayTransport />
</binding>
</customBinding>
</bindings>
<services>
<service name="PSS">
<endpoint name="DuplexRelayEndpoint"
contract="PSC"
binding="customBinding" bindingNamespace="http://thinktecture.com/services/relay/pubsub"
bindingConfiguration="duplexRelay" />
</service>
</services>
</system.serviceModel>
</configuration>
I will skip the service hosting code, it is merely about fiddling with the endpoint address setup as I am runnin gthe servive and the client on the same machine and want to dynamically have the machine name in the endpoint address so that the relay service can register a unique address for the relaying.
Half time.
Now on to the consuming side. We similarily need to have the custom duplex
binding but now want to explicitly specify the endpoint address for the
callback. We will do this inside of the client code.
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name ="duplexRelay">
<compositeDuplex clientBaseAddress="SeeCode" />
<oneWay />
<binaryMessageEncoding />
<relayTransport />
</binding>
</customBinding>
</bindings>
<client>
<endpoint name="RelayEndpoint"
contract="PSC"
binding="customBinding"
bindingConfiguration="duplexRelay"
address="SeeCode" />
</client>
</system.serviceModel>
</configuration>
This is the code to set the client's base callback address dynamically in code, derived from what I have shown here.
CustomBinding binding = (CustomBinding)channelFactory.Endpoint.Binding;
CompositeDuplexBindingElement duplex = binding.Elements.Find<CompositeDuplexBindingElement>();
Uri clientAddress = new Uri(
String.Format(
"net.relay://{0}/services/{1}/PubSub/callback/",
RelayBinding.DefaultRelayHostName,
GetHostName()));
duplex.ClientBaseAddress = clientAddress;
One thing we need to make sure with the custom duplex binding it to have the WS-Addressing ReplyTo header explicitly set on the outgoing message.
using (new OperationContextScope((IContextChannel)channel))
{
Console.WriteLine("Calling Subscribe...");
OperationContext.Current.OutgoingMessageHeaders.ReplyTo =
((IClientChannel)channel).LocalAddress;
channel.Subscribe(input);
}
Likewise, on the service side, we need to add the To header to the outgoing message traveling to the original caller, i.e. the client.
public void Subscribe(string subMessage)
{
Console.WriteLine("Subscribed...: {0}", subMessage);
IPubSubCallback callback =
OperationContext.Current.GetCallbackChannel<IPubSubCallback>();
if (callback != null)
{
OperationContext.Current.OutgoingMessageHeaders.To =
OperationContext.Current.IncomingMessageHeaders.ReplyTo.Uri;
callback.Publish("Huhu!");
}
}
To make a rather long story short, this is what happens when we run both the
service and the client. We also tested this between Sri Lanka and Germany :)
Buddhike was running the
service and I the client: worked, I could reach his service and his service was
calling back into my client - everything behind firewalls and NATs.
Everything happens in a safe fashion as we need to authenticate ourselves with a
registered self-issued InfoCard for communication.
Oh, BTW, there is currently one caveat with my approach and the code: the
first time the service wants to call back into the client some human being needs
to select the appropriate card. This is by design of the current
netRelayBinding from the BizTalk Services SDK
and surely by design of CardSpace.
I am still investigating.
Good blog! You saved my time. Thanks! http://sexy-christi-shake-nude-photo.org
Posted by: christi | 05/08/2007 at 05:09 PM
Christian, First of all Biztalk services does not work with corporate firewalls (which does not allow outbound tcp connection), second if you want to use Biztalk services, then the colud of servers would be under control of Microsoft, basically Microsoft gives you client side SDK so you have to rely Microsoft's infrastructure rather than yours. The better solution is JXTA (https://jxta.dev.java.net/ https://guest@jxta-guide.dev.java.net/svn/jxta-guide/trunk/src/guide_v2.5/JXSE_ProgGuide_v2.5.pdf).
Posted by: BIjan | 01/28/2008 at 12:27 AM
Very helpful! Thanks for your good work!
Posted by: Acai Optimum | 01/19/2010 at 08:00 AM
Wow, that's a lot of coding. Really interesting that you can get a safe and secure connection like that.
Posted by: Cho Yung Tea | 04/01/2010 at 07:01 AM
Very informational article.....The only thing nodes outside of the NAT-shielded network can see is the IP address of the NAT-capable router device.
Posted by: Work From Home | 04/15/2010 at 05:19 AM
Every body admits that our life seems to be not cheap, however different people require money for different issues and not every man earns big sums cash. So to get some loan and just commercial loan will be a proper solution.
Posted by: FayeEnglish26 | 06/09/2010 at 01:23 PM
You have got some great posts in your blog. I will be visiting again.
Posted by: otoplastieken muziek | 06/20/2010 at 06:18 PM
Well don't know whats going on but its not a Good way to do this. in my opinion we have to look again about this issue
Posted by: company logo design | 07/26/2010 at 09:24 AM
Good job.No matter where we are, we must study all the way. As the proverb says that: You are never too old to learn. Thank you for your blog.
Posted by: Supra Shoes | 09/08/2010 at 09:50 AM
Very interesting to share such writings.I really gathered much from this.Thanks for sharing.
Posted by: mini clip | 09/11/2010 at 05:54 PM
Nice post.I’m really impressed with your article, such great information you mentioned here..
Posted by: free mahjong | 09/11/2010 at 05:54 PM
Dare and the world always yields. If it beats you sometimes, dare it again and again and it will succumb. Do you think so?
Posted by: New Balance Shoes | 09/13/2010 at 12:10 PM
I was reading your course titles and thought how ironic your interest in you said,and I have some ideas about it,and then you link my name ,I will tell you the detail.Thanks a lot for the wonderful information.I will follow you the posts you write in the future.
Posted by: Retro Jordan | 10/09/2010 at 11:56 AM
So wonderful posts,like all them.I think you are a happy one.don't stop writing so good post.hope you will get more and more celebrites from other people.thanks for your thoughts about something.
Posted by: jordan 7 | 10/25/2010 at 10:48 AM
There is no doubt that your blog is specific,I've learnt a lot from you. Something of your blog informations are very helpful to us. WISH YOU HAPPY EVERYDAY!ASHGDJFH.
Posted by: air jordan 6 | 10/30/2010 at 05:29 AM
Green Leaf is excellent, imo.
Posted by: nike air max | 11/12/2010 at 10:08 AM
oh so funny!
Posted by: UGG Shoes | 11/13/2010 at 03:03 AM
I did not discuss that particular issue!!
Posted by: moncler jackets | 11/16/2010 at 02:17 AM
*Good health, like air, is essential to human beings!*
Posted by: air jordan | 11/16/2010 at 04:41 AM
I like your concept.*_*
Posted by: nike air max | 11/17/2010 at 08:56 AM
Nike Air Jordan Retro for Sell, Nike Air Jordan retro shoes, arks and Recreation is holding the Monte Cristo Snowmobile Open Air Demo and Safety Fair Jan. 15 from 9 a.m. to 3 p.m. at the Monte Cristo Trailhead. Visitors can test ride new snowmobiles, learn or hone avalanche beacon skills and see …
Posted by: cheap jordan spizikes | 12/29/2010 at 06:56 AM
It's a Very helpful article for me. Actually, I am fond of reading online punjabi news. Thanks for writing such a complete ..And,I wantn't to miss them.
Thank you for sharing..
Posted by: Thank | 01/05/2011 at 07:22 AM
Finally found what I want, I'm a beginner content inside of great help to me with you, share
Posted by: thomas sabo | 01/20/2011 at 03:15 AM
very interesting. No matter where we are, we must study all the way. As the proverb says that: You are never too old to learn. Thank you for your blog.
Posted by: Sandra | 03/29/2011 at 01:17 PM
Every new visit i have always a new stuff of information in your blog. Really your blog, is marvelous. Thanks for stuff.
Posted by: banners design | 04/04/2011 at 01:18 PM