DCSIMG
February 2012 - Posts - Ido Flatow's Blog Veni Vidi Scripsi

Ido Flatow's Blog

Veni Vidi Scripsi

News

Have you heard me speak?
Powered
<style type='text/css' media='screen' id='sm_css'> #smix {overflow: visible;height: auto;border-radius: 10px;max-width: 250px;background-color: #323232;text-align: left;font-size: 12px;line-height: 16px;font-family:'Lucida Sans Unicode','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;-webkit-border-radius: 10px;-moz-border-radius: 10px;border-radius: 10px;} #smix a {color: #0056CC;text-decoration: none;} #smix .sm_head {color: #fff; line-height: 1em;font-size: 1.4em;padding: 10px;color: #fff;} #smix .sm_lanyard_wrapper {background-color: #fff;;clear: both;width: 97%;margin: 0 auto;margin-bottom: 0px;} #smix .sm_lanyard_content {padding: 7px;}#smix button.sm_rec, #smix a.sm_rec, #smix input[type=submit].sm_rec { padding: 6px 10px; -webkit-border-radius: 2px 2px;-moz-border-radius: 2px; border-radius: 2px; border: solid 1px rgb(153, 153, 153); background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(255, 255, 255)), to(rgb(221, 221, 221))); color: #333; text-decoration: none; cursor: pointer; display: inline-block; text-align: center; text-shadow: 0px 1px 1px rgba(255,255,255,1); line-height: 1; }#smix .sm_rec:hover { background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(248, 248, 248)), to(rgb(221, 221, 221))); }#smix .sm_rec:active { background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(204, 204, 204)), to(rgb(221, 221, 221))); }#smix .sm_rec.medium { padding: 3px 7px; font-size: 13px; }#smix .sm_rec span.icon.thumbs_up {background-position: 0px 36px;vertical-align: text-top;display: inline-block;margin-right: 4px;height: 18px;width: 16px;background-image: url(http://speakermix.com/images/new/thumbsold.png);}#smix .sm_rec:hover span.icon.thumbs_up {background-position: 0px 18px;} #smix .sm_events {padding:2px 0px 4px 0px;} #smix .sm_section {font-size: 10px; border-bottom: 1px solid silver; margin-bottom: 6px;} #smix .sm_subline {font-size:120%;margin-top:4px;font-weight:bold} #smix .powered {text-align: right} #smix .powered img {margin: 7px} </style>
Sela Technology Center

Advertisement

February 2012 - Posts

What’s new in WCF 4.5? UDP transport support

This is the tenth post in the WCF 4.5 series. I’ve started this series of posts 4 months ago when .NET 4.5 developer preview was announced; The Beta/RC/RTM version is still to come, but hopefully it will be available soon, and you will be able to use the new WCF 4.5 features in your projects.

Until now, I’ve shown new features in configuration easiness and hosting improvements. In this post and the next one I will cover new transport features, starting with the support for the UDP transport.

Previous posts:

1. What’s new in WCF 4.5? let’s start with WCF configuration

2. What’s new in WCF 4.5? a single WSDL file

3. What’s new in WCF 4.5? Configuration tooltips and intellisense in config files

4. What’s new in WCF 4.5? Configuration validations

5. What’s new in WCF 4.5? Multiple authentication support on a single endpoint in IIS

6. What’s new in WCF 4.5? Automatic HTTPS endpoint for IIS

7. What’s new in WCF 4.5? BasicHttpsBinding

8. What’s new in WCF 4.5? Changed default for ASP.NET compatibility mode

9. What’s new in WCF 4.5? Improved streaming in IIS hosting

I’ve been teaching WCF for several years now, and almost every time I explain to people about the different types of bindings and supported transports someone asks me if there is a built-in support for a UDP transport. Until now my answer was “It isn’t supported out-of-the-box, but there is a UDP transport sample in the WCF/WF samples”. From now on my new answer is “In WCF 4/3.5 there is a sample implementation, but it is now out-of-the-box in WCF 4.5”.

You can get some basic info about the binding in the System-Provided Bindings page on MSDN (make sure you look at the 4.5 version), unfortunately there is no documentation on the UdpBinding type yet, but hopefully MS will create it by the time WCF 4.5 is released.

Declaring an endpoint that uses UDP is quite simple:

<endpoint address="soap.udp://localhost:8080/" binding="udpBinding" contract="UdpHost.IService" />

Some facts about the new UDP binding:

  1. The address scheme for this binding is soap.udp://
  2. The binding is supported only by .NET (since it does uses text encoding for the SOAP messages, it can be considered interoperable, although currently no other SOAP-based services technology support UDP)
  3. Security is not supported (neither transport or message)
  4. Sessions, transactions, streaming, and duplex are not supported (I was hoping for a duplex UDP using one-way messages)
  5. Supported encoding is text
  6. The binding can be used in code by adding a reference to the System.ServiceModel.Channels assembly
  7. The binding is not supported in IIS/WAS, since there is still no UDP shared listener for WAS
  8. In the case of One-Way messages, this is a true one-way unidirectional call - the client won’t throw an exception if the service is unavailable
  9. If you specify a multicast address in the endpoint, such as 224.0.0.1 or 239.255.255.255 (the later is used by UDP discovery endpoints), you can create multiple listeners on the same address+protocol even from different machines – one client can send a single message that will be received by multiple listeners – this can be a great way to synchronize server state, do pub-sub (notification) calls etc...

Using a multicast listening address is also quite simple:

<endpoint address="soap.udp://239.255.255.255:8083/" binding="udpBinding" contract="UdpHost.IService" />

As for performance, I’ve create a simple client which sends 5000 messages using request-response and one-way (simple and multicast), with HTTP (basicHttp), TCP, and UDP bindings. For the purpose of the test I’ve removed all security settings from the NetTcp binding. The result is shown below:

Calling 5000 iterations one way using UDP
One-way using UDP took 0 seconds, average is 0.1792ms

Calling 5000 iterations one way using HTTP
One-way using HTTP took 6 seconds, average is 1.3ms

Calling 5000 iterations one way using TCP
One-way using TCP took 2 seconds, average is 0.5876ms

Calling 5000 iterations one way using UDP Multicast
One-way using UDP Multicast took 3 seconds, average is 0.736ms

Calling 5000 iterations of request-response using UDP
Request-response using UDP took 7 seconds, average is 1.4738ms

Calling 5000 iterations of request-response using HTTP
Request-response using HTTP took 8 seconds, average is 1.7784ms

Calling 5000 iterations of request-response using TCP
Request-response using TCP took 4 seconds, average is 0.9518ms

Conclusions:

  1. One-way messages are fast when using UDP – about 7 times faster than HTTP, and 3 times faster than TCP. I assume this is because when using UDP we don’t need to wait for the TCP ACK (also used in HTTP).
  2. One-way multicast messages in UDP are slower than direct messages – about 4 times slower
  3. Request-response message in UDP are slower than TCP, but faster than HTTP. I assume the reason for being slower than TCP is that UDP uses two channels to create request-response whereas TCP requires only one channel.

Trying to run the same sample when the server has not been started produces the following results:

Calling 5000 iterations one way using UDP
One-way using UDP took 0 seconds, average is 0.1216ms

Calling 5000 iterations one way using HTTP
One-way using HTTP failed

Calling 5000 iterations one way using TCP
One-way using TCP failed

Calling 5000 iterations one way using UDP Multicast
One-way using UDP Multicast took 1 seconds, average is 0.376ms

Calling 5000 iterations of request-response using UDP
Request-response using UDP failed

Calling 5000 iterations of request-response using HTTP
Request-response using HTTP failed

Calling 5000 iterations of request-response using TCP
Request-response using TCP failed

As you can see, UDP one-way messages are unidirectional!!!

The sample code is available on my SkyDrive.

Calling a WCF service from a client without having the contract interface

I was asked yesterday in the Hebrew C#/.NET Framework MSDN forums a tough question – is it possible to dynamically call a WCF service using only the contract name, operation name, and metadata address?

At first I agreed with the answer given in the forum – move from SOAP bindings to WebHttpBinding (“REST”). This of course makes things a lot easier, only requiring you to create a WebHttpRequest and parse the response. However the question remains - is it possible to do this in the case of a SOAP-based service endpoint?

The short answer is – YES!

The full answer is – YES, but you’ll need to do a lot of coding to make it work properly, and even more coding for complex scenarios (who said passing a data contract?)

How is it done you ask?

First let’s start with the contract – you have a simple contract that looks like so:

   1:  [ServiceContract]
   2:  public interface ICalculator
   3:  {
   4:    [OperationContract]
   5:    double Add(double n1, double n2);
   6:    [OperationContract]
   7:    double Subtract(double n1, double n2);
   8:    [OperationContract]
   9:    double Multiply(double n1, double n2);
  10:    [OperationContract]
  11:    double Divide(double n1, double n2);
  12:  }

At this point the implementation doesn’t matter, but you can assume the service compiles and loads successfully.

Second, make sure your service has either a MEX endpoint or metadata exposed over HTTP GET. Read here for more info about the difference between the two.

Third – do the client coding!!! to create the client code I took some ideas from the following links:

http://msdn.microsoft.com/en-us/library/ms733780.aspx – generating client-side type information for WCF contracts

http://www.codeproject.com/Articles/42278/Call-a-Web-Service-Without-Adding-a-Web-Reference – the same concept of dynamic calls, but for ASP.NET web services (ASMX).

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.ServiceModel;
   5:  using System.ServiceModel.Description;
   6:  using System.Globalization;
   7:  using System.Collections.ObjectModel;
   8:  using System.CodeDom.Compiler;
   9:   
  10:  namespace Client
  11:  {
  12:      class Program
  13:      {
  14:          static void Main(string[] args)
  15:          {
  16:              // Define the metadata address, contract name, operation name, and parameters. 
  17:              // You can choose between MEX endpoint and HTTP GET by changing the address and enum value.
  18:              Uri mexAddress = new Uri("http://localhost:8732/CalculatorService/?wsdl");
  19:              // For MEX endpoints use a MEX address and a mexMode of .MetadataExchange
  20:              MetadataExchangeClientMode mexMode = MetadataExchangeClientMode.HttpGet;            
  21:              string contractName = "ICalculator";
  22:              string operationName = "Add";
  23:              object[] operationParameters = new object[] { 1, 2 };
  24:   
  25:              // Get the metadata file from the service.
  26:              MetadataExchangeClient mexClient = new MetadataExchangeClient(mexAddress, mexMode);
  27:              mexClient.ResolveMetadataReferences = true;
  28:              MetadataSet metaSet = mexClient.GetMetadata();
  29:   
  30:              // Import all contracts and endpoints
  31:              WsdlImporter importer = new WsdlImporter(metaSet);
  32:              Collection<ContractDescription> contracts = importer.ImportAllContracts();
  33:              ServiceEndpointCollection allEndpoints = importer.ImportAllEndpoints();
  34:   
  35:              // Generate type information for each contract
  36:              ServiceContractGenerator generator = new ServiceContractGenerator();
  37:              var endpointsForContracts = new Dictionary<string, IEnumerable<ServiceEndpoint>>();
  38:   
  39:              foreach (ContractDescription contract in contracts)
  40:              {
  41:                  generator.GenerateServiceContractType(contract);
  42:                  // Keep a list of each contract's endpoints
  43:                  endpointsForContracts[contract.Name] = allEndpoints.Where(
  44:                      se => se.Contract.Name == contract.Name).ToList();
  45:              }
  46:   
  47:              if (generator.Errors.Count != 0)
  48:                  throw new Exception("There were errors during code compilation.");
  49:   
  50:              // Generate a code file for the contracts 
  51:              CodeGeneratorOptions options = new CodeGeneratorOptions();
  52:              options.BracingStyle = "C";
  53:              CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("C#");
  54:   
  55:              // Compile the code file to an in-memory assembly
  56:              // Don't forget to add all WCF-related assemblies as references
  57:              CompilerParameters compilerParameters = new CompilerParameters(
  58:                  new string[] { 
  59:                      "System.dll", "System.ServiceModel.dll", 
  60:                      "System.Runtime.Serialization.dll" });
  61:              compilerParameters.GenerateInMemory = true;
  62:   
  63:              CompilerResults results = codeDomProvider.CompileAssemblyFromDom(
  64:                  compilerParameters, generator.TargetCompileUnit);
  65:   
  66:              if (results.Errors.Count > 0)
  67:              {
  68:                  throw new Exception("There were errors during generated code compilation");
  69:              }
  70:              else
  71:              {
  72:                  // Find the proxy type that was generated for the specified contract
  73:                  // (identified by a class that implements the contract and ICommunicationbject)
  74:                  Type clientProxyType = results.CompiledAssembly.GetTypes().First(
  75:                      t => t.IsClass &&
  76:                          t.GetInterface(contractName) != null &&
  77:                          t.GetInterface(typeof(ICommunicationObject).Name) != null);
  78:                          
  79:                  // Get the first service endpoint for the contract
  80:                  ServiceEndpoint se = endpointsForContracts[contractName].First();                    
  81:   
  82:                  // Create an instance of the proxy
  83:                  // Pass the endpoint's binding and address as parameters
  84:                  // to the ctor
  85:                  object instance = results.CompiledAssembly.CreateInstance(
  86:                      clientProxyType.Name, 
  87:                      false, 
  88:                      System.Reflection.BindingFlags.CreateInstance, 
  89:                      null,
  90:                      new object[] { se.Binding, se.Address }, 
  91:                      CultureInfo.CurrentCulture, null);
  92:                  
  93:                  // Get the operation's method, invoke it, and get the return value
  94:                  object retVal = instance.GetType().GetMethod(operationName).
  95:                      Invoke(instance, operationParameters);
  96:   
  97:                  Console.WriteLine(retVal.ToString());
  98:              }
  99:          }
 100:      }
 101:  }

I’ve placed comments that describe the code, but basically it imports the WSDL, generates types for the contract (service + data), generates C# code from it, compiles it, and uses reflection to create a proxy and invoke the correct method.

If you want to use this technique to call methods that require a data contract, you will need some extra work to create the correct type and initialize it.

A compiled and running version of this code (+ the service) can be found here: https://skydrive.live.com/redir.aspx?cid=5ef5be1ab30a6056&resid=5EF5BE1AB30A6056!466&parid=5EF5BE1AB30A6056!129

Hope you find this piece of code useful.

Feb-12:

Just found out this great blog post that uses the same implementation only to enable calling WCF services from PowerShell. 
http://www.justaprogrammer.net/2012/02/11/using-powershell-to-call-a-wcf-service/