Jaa


Enabling cross-domain calls for SL apps on self-hosted TCP services

On a post a couple of years back, I showed a solution to enable cross-domain calls for Silverlight apps on self-hosted WCF services. In Silverlight 4, we added a new transport (net.tcp), which needs its own policy file as well (see more details at https://blogs.msdn.com/b/silverlightws/archive/2010/04/09/policy-file-for-nettcp.aspx). If you are running IIS (or some other web service) on port 80 the machine on which the TCP service is running, the easiest way to deploy the appropriate policy file is to simply copy the file to its root directory (in IIS it would be something like c:\inetpub\wwwroot), and you're ready to go.

If you don't have IIS, however, you can use a similar solution than the one I had for "normal" (i.e., HTTP-based) WCF services to serve the policy file for the TCP service. Again, if you have IIS (or other web service running on port 80), you don't need to do this, just drop the policy file on the server root directory and you're good to go. Essentially, the code will act as a web server and serve the "policy file" to SL apps when requested. The base address for the WCF REST service would be listening at port 80 on the machine, and its endpoint would be listening at the "" address. Here's the code:

  1. namespace SelfHostedTcpAndSilverlight
  2. {
  3.     [ServiceContract]
  4.     public interface ITest
  5.     {
  6.         [OperationContract]
  7.         string Echo(string text);
  8.     }
  9.     [ServiceContract]
  10.     public interface ITcpPolicyRetriever
  11.     {
  12.         [OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
  13.         Stream GetSilverlightPolicy();
  14.     }
  15.     public class Service : ITest, ITcpPolicyRetriever
  16.     {
  17.         public string Echo(string text)
  18.         {
  19.             return text;
  20.         }
  21.         public Stream GetSilverlightPolicy()
  22.         {
  23.             string result = @"<?xml version=""1.0"" encoding=""utf-8""?>
  24. <access-policy>
  25.     <cross-domain-access>
  26.         <policy>
  27.             <allow-from http-request-headers=""*"">
  28.                 <domain uri=""*""/>
  29.             </allow-from>
  30.             <grant-to>
  31.                 <socket-resource port=""4504"" protocol=""tcp"" />
  32.             </grant-to>
  33.         </policy>
  34.     </cross-domain-access>
  35. </access-policy>";
  36.             WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
  37.             return new MemoryStream(Encoding.UTF8.GetBytes(result));
  38.         }
  39.     }
  40.     class Program
  41.     {
  42.         static void Main(string[] args)
  43.         {
  44.             string baseAddressHttp = "https://" + Environment.MachineName + ":80";
  45.             string baseAddressTcp = "net.tcp://" + Environment.MachineName + ":4504/Service";
  46.             ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddressHttp), new Uri(baseAddressTcp));
  47.             host.AddServiceEndpoint(typeof(ITest), new NetTcpBinding(SecurityMode.None), "");
  48.             host.AddServiceEndpoint(typeof(ITcpPolicyRetriever), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
  49.             ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
  50.             host.Description.Behaviors.Add(smb);
  51.             host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
  52.             host.Open();
  53.             Console.WriteLine("Host opened");
  54.             Console.Write("Press ENTER to close");
  55.             Console.ReadLine();
  56.             host.Close();
  57.         }
  58.     }
  59. }

Comments

  • Anonymous
    September 25, 2010
    Is that a typo for baseAddressHttp? Should it be string baseAddressHttp = "http://" + Environment.MachineName + ":80"; instead?
  • Anonymous
    September 25, 2010
    Just confirmed that it is a typo. This works:string baseAddressHttp = "http://" + Environment.MachineName + ":80";BTW, thanks for this. Helped a lot.
  • Anonymous
    September 25, 2010
    Thanks Jonathan, the <a> tag was added by my blog writer. Updated it inline.
  • Anonymous
    October 09, 2010
    Another question. This solution seems to commandeer any site already being served on port 80. Is there another way to prevent a takeover of "http://" + Environment.MachineName + ":80"?
  • Anonymous
    October 19, 2010
    This example assumes that you don't have a web server listening at port 80. If you already have a service listening at that port (such as IIS), you can add the policy file in that server (again, at the root), and simply have the self-hosted TCP service to listen to SL clients in this process.
  • Anonymous
    November 09, 2010
    Can you please provide me the things need to do in servicereferences.clientconfig of the silverlight app?In Silverlight App, when I add a ServiceReference ro this WCF service which is running in windows service, it is discovering the service properly and generating necessary files (proxy, classes etc) to the project but it is not writing anything in servicereferences.clientconfig.Can you please provide me the necessary settings we need to do in SL?I have been dying on this for 1 month now.
  • Anonymous
    November 20, 2010
    When you add the service reference, is there any error / warning message shown in visual studio? If there are no endpoints compatible with Silverlight in your WCF service, you may see this problem.Another thing you can try to do is to run slsvcutil.exe against the service, and see what is generated on the ServiceReferences.ClientConfig from that.
  • Anonymous
    December 15, 2010
    Hi Carlos, thanks to your sample I now have a working silverlight application that talks to a self-hosting TCP service wpf application.This is the picture: intranet with n silverlight clients and 1 self-hosting TCP service wpf application that works as a server. Everything is wonderful if the clients are running on a windows machine, but I can't make the service work on a  MacBook Pro connected in the very same intranet. Everything looks fine:I can ping the Macbook from the server machine, and I can ping back the server machine from the macbook. I can see the shared folders of the server machine from the macbook, nonetheless I can't see anything coming back from the wcf via net.tcp service.My question is:Is it possible at all a similar scenario (an intranet with Mac's and Pc's living together and using the same WCF service via net.tcp)?.Do you have a hint of what could make it work?Best RegardsRoberto Dalmonte
  • Anonymous
    December 16, 2010
    Roberto, I'm able to access WCF services with Net.TCP from SL running on macs as well at work, but I remember that there was some IPSec permission that I needed to request from the network administrators to allow my (PC) machine to be seen from macs. It's possible that this permission is limited to a certain range of ports, so you can check that.One way to find out if the requests are going from the mac to the PC would be to install network capture softwares in both machines (I use Cocoa packet analyzer for Macs and Netmon for Windows). You should see some TCP packets with the SYN flag set (to set up the communication) to the server port (which would be also a good filter to use in the capture programs, to prevent too much noise from making the analysis harder) - which in the case of the example I have here is 4504. You can also fiter to capture packets from port 80, to see if the request to the policy is being made. Once you have the captures from both Mac and PC, you'll be able to tell whether the request was sent by the mac, and was received by the PC. If the latter didn't happen, then it's possible that you may have some permissions issue on your network.
  • Anonymous
    December 16, 2010
    The comment has been removed
  • Anonymous
    April 25, 2011
    Hello Carlos.Sorry for ressurect your post...I have implemented your example and it works fine. But I would like to host my WebHttpBinding endpoint in a port different of 80.Is it possible?My goal is to avoid the port 80 because it is a "common" port. For example: my endpoint don't properly hosts if Skype is in use (if I start the Skype after the service, it works properly).Thanks.
  • Anonymous
    April 25, 2011
    Hi Flavio,The policy file used for WCF TCP services in Silverlight is always hosted at port 80, so you can't use another port. If you have IIS running on the machine, however (and IIRC you can have IIS from most windows versions, starting from XP), you can simply start it, drop the policy file in the IIS root and it should work as well.Hope this helps!
  • Anonymous
    April 25, 2011
    Thanks for your quick answer, Carlos.Well, so I think there is nothing much to be done... There'll must to be done this way.See You Later.
  • Anonymous
    September 07, 2011
    Carlos - thank you this was helpful. I would say if you ever feel like it, that expanding in your original text on this blog entry in regards to Port 80 getting 'commandeered' as another poster added would be helpful. In my case I was self-hosting a net.tcp service in a Windows Service, and when I served up the clientaccesspolicy as you have laid out on Port 80, all of my IIS hosted WCF services on that port stopped working with a 'No Endpoint Found' error. And try to use your solution to host the clientaccesspolicy on the same port used by the net.tcp binding service instead of port 80 and you will get an error stating that the port is already used. That only works if the WCF service is using a Http binding.I was a bit confused by the original post and this one that Windows Services CAN actually access a statically placed clientaccesspolicy.xml file in 'wwwroot'. I thought this was the entire purpose of serving up the policy in a WCF service was because self-hosted services could not see the statically placed files in IIS directories. Like it was a security issue with a IIS sandbox or something and Win Services could not get to it.This solution really totally revolves around server with self-hosted WCF services that do not have IIS installed or running, and I must have missed that concept. I went down a long path to come all the way back and realize that the Windows Service hosted WCF Service can indeed see that clientaccesspolicy.xml if IIS is running. Made it a lot simpler too. Thanks again your (2) links have helped.
  • Anonymous
    September 07, 2011
    Thanks for the comment. I've updated the post to emphasize that you only need to do it if you don't have IIS installed in the machine where the TCP server is running.
  • Anonymous
    October 10, 2011
    Hi, i try your solution with Silverlight 4, self-hosted WCF, basicHttpBinding, and with IIS stop, but still getting the same error while debugging my silverlight apps using Visual Studio 2010, any suggestion? thanks
  • Anonymous
    October 10, 2011
    Kevan, if you're using BasicHttpBinding you aren't dealing with TCP services (the theme of this post). Take a look at the post at blogs.msdn.com/.../enabling-cross-domain-calls-for-silverlight-apps-on-self-hosted-web-services.aspx for information on HTTP services.
  • Anonymous
    March 14, 2012
    Can you provide a snippet like this for a SSL HTTPS connection instead of the http?Thanks!
  • Anonymous
    March 17, 2012
    Chris, the SL policy needs to be served over port 80 (plain HTTP) to enable TCP for Silverlight. HTTPS cannot be used for that. Notice that only the request for client access policy file to allow SL to make TCP requests need to be made over HTTP. You can have services over HTTPS without problems.
  • Anonymous
    June 22, 2012
    Hi,You need to place the clientaccesspolicy.xml file in the root folder on the server. Also you might have to add an exception(in windows firewall on the server side) for the port which you are going to use.For more information:forums.silverlight.net/.../1Sample:iconnect.arshdeep-virdi.com/webThanks,Arsh
  • Anonymous
    July 01, 2012
    What could override this behavior?  Silverlight 5 is asking for clientaccesspolicy.xml on my net.tcp port (4530) not port 80.  If I browse to 127.0.01:80/clientaccesspolicy.xml - I see the xml as expected.  SL is just looking in the wrong spot for it...Thank you,-Chad