Galin Iliev's blog

Software Architecture & Development

Hosting WCF service over net.tcp protocol in IIS7 (WAS)

There are several articles showing how to host non-http protocols on IIS7. Some steps are like black box while others could easily be misconfigured. 

The tasks is complicated additionally by obscured error messages returned in the browser :)

So let’s started (the example below is in Windows Server 2008. In Vista/Windows7 is similar but not exactly same):

Phase 1: Prepare server (prerequisites)

1. Add Web Server Role

  • Select following features as a minimum:
  • Common HTTP Features - all
  • Application Development
  • ASP.NET
  • .NET Extensibility
  • ISAPI extensions
  • ISAPI Filters
  • Security – all

2. Add Application Role

Add Application Server role and select all features (confirm all dependencies):

image

With this Phase 1 is complete. We should be able to navigate to Internet Information Services (IIS) Manage console in Administrative tools.

Phase 2: Deploying WCF application

1. Build WCF application with netTcpBinding :) (kindda obvious). For service endpoints leave address relative or empty.

   1: <services>
   2:   <service name="Microsoft.Test.Service" behaviorConfiguration="Service.ServiceBehavior">
   3:     <!-- Service Endpoints -->
   4:     <endpoint name="netTcp" address="" binding="netTcpBinding" bindingConfiguration="bigMessages" contract="Microsoft.Test.IService" />
   5:     <endpoint name="mexNetTcp" address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
   6:   </service>
   7: </services>

2. Create website that will host it in IIS

Since it is not a challenge to create it via IIS Manager here is how it can be achieved via script (JS). This can be used as custom action in WiX/MSI as well

   1: var adminManager = new ActiveXObject('Microsoft.ApplicationHost.WritableAdminManager');
   2: adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST";
   3:  
   4: var sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST");
   5:  
   6: var sitesCollection = sitesSection.Collection;
   7:  
   8: var siteElement = sitesCollection.CreateNewElement("site");
   9: siteElement.Properties.Item("name").Value = "WcfNetTcp";
  10: siteElement.Properties.Item("id").Value = 7;
  11:  
  12: var bindingsCollection = siteElement.ChildElements.Item("bindings").Collection;
  13:  
  14: var bindingElement = bindingsCollection.CreateNewElement("binding");
  15: bindingElement.Properties.Item("protocol").Value = "http";
  16: bindingElement.Properties.Item("bindingInformation").Value = "*:80:";
  17: bindingsCollection.AddElement(bindingElement);
  18:  
  19: var siteCollection = siteElement.Collection;
  20:  
  21: var applicationElement = siteCollection.CreateNewElement("application");
  22: applicationElement.Properties.Item("path").Value = "D:\\Hosting\\WcfApp";
  23: applicationElement.Properties.Item("applicationPool").Value = "DefaultAppPool";
  24: siteCollection.AddElement(applicationElement);
  25:  
  26: sitesCollection.AddElement(siteElement);
  27:  
  28: adminManager.CommitChanges();
  29:  

3. Add net.tcp protocol:

Here is how it should look from GUI:

image

And the script for it

   1: var adminManager = new ActiveXObject('Microsoft.ApplicationHost.WritableAdminManager');
   2: adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST";
   3:  
   4: var sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST");
   5:  
   6: var sitesCollection = sitesSection.Collection;
   7:  
   8: var siteElementPos = FindElement(sitesCollection, "site", ["name", "WcfNetTcp"]);
   9: if (siteElementPos == -1) throw "Element not found!";
  10: var siteElement = sitesCollection.Item(siteElementPos);
  11:  
  12:  
  13: var bindingsCollection = siteElement.ChildElements.Item("bindings").Collection;
  14:  
  15: var bindingElement = bindingsCollection.CreateNewElement("binding");
  16: bindingElement.Properties.Item("protocol").Value = "net.tcp";
  17: bindingElement.Properties.Item("bindingInformation").Value = "809:*";
  18: bindingsCollection.AddElement(bindingElement);
  19:  
  20: adminManager.CommitChanges();

4. Enable net.tcp for website (Virtual Application) that hosts Wcf Service – in this example we’re hosting in the root of website

image

And of course the script:

   1: var adminManager = new ActiveXObject('Microsoft.ApplicationHost.WritableAdminManager');
   2: adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST";
   3:  
   4: var sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST");
   5:  
   6: var sitesCollection = sitesSection.Collection;
   7:  
   8: var siteElementPos = FindElement(sitesCollection, "site", ["name", "WcfNetTcp"]);
   9: if (siteElementPos == -1) throw "Element not found!";
  10: var siteElement = sitesCollection.Item(siteElementPos);
  11:  
  12: var siteCollection = siteElement.Collection;
  13:  
  14: var applicationElementPos = FindElement(siteCollection, "application", ["path", "/"]);
  15: if (applicationElementPos == -1) throw "Element not found!";
  16: var applicationElement = siteCollection.Item(applicationElementPos);
  17:  
  18: applicationElement.Properties.Item("enabledProtocols").Value = "http,net.tcp";
  19:  
  20: adminManager.CommitChanges();

Warning: Do not put any spaces in “Enabled Protocols” field. Although it might works for HTTP it doesn’t for net.tcp.

5. (Optional) Run in elevated mode (As Administrator):

   1: “%WINDIR%\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\ServiceModelReg.exe" –i

I will cover why it is optional in next posts.

Note: Scripts are generated using IIS 7.5 IIS Management console and Windows 7

Hope that helps.

P.S. Here is the helper js function FindElement(). It is slightly modified from original generated by IIS Manager so it will compare values in case insensitive way.

Comments (1) -

Loading