Fino alla versione 3.0 l’unica tipologia di transport supportata da Silveright nei servizi WCF era l’HttpTransport nelle due varianti: BasicHttpBinding o la versione dove TextMessageEncoding viene sostituito da BinaryMessageEncoding migliorando leggermente le prestazioni.
In Silverlight 4.0 è stato aggiunto il supporto a TcpTransport ovvero la possibilità di interagire con endpoints che utilizzando NetTcpBinding migliorando,non poco, sopratutto in contesti Intranet la velocità di comunicazione.

Visto che la procedura da seguire non è proprio “intuitiva” ecco un piccolo tutorial:

Creazione del servizio

In questo caso non possiamo usare il webserver integrato in Visual Studio 2010 in quanto non supporta il protocollo net.tcp, dobbiamo quindi ospitare il servizio in IIS7 il quale grazie al servizio WAS (Windows Activation Service) è in grado di indirizzare chiamate via Tcp-Ip al servizio WCF.
Eseguiamo Add->New WebSite->WCF Service

image

Implementiamo ora la funzionalità del servizio: in questo caso il servizio non faremo altro che ritornare una collezione di oggetti Info in base all’identificativo passato al metodo GetInfo()

   1: [ServiceContract]
   2: public interface IMyService
   3: {
   4:     [OperationContract]
   5:     List<Info> GetInfo(int value);    
   6: }
   7:  
   8: [DataContract]
   9: public class Info
  10: {   
  11:    public int Id { get; set; }
  12:    public string Name { get; set; }
  13: }
  14:  
  15: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
  16: public class Service : IMyService
  17: {
  18:    List<Info> infos = new List<Info>();
  19:    public Service()
  20:    {
  21:       this.CreateInfo();
  22:    }
  23:  
  24:    private void CreateInfo()
  25:    {
  26:       infos.Add(new Info() { Id = 1, Name = "Info1" });
  27:       infos.Add(new Info() { Id = 2, Name = "Info2" });
  28:       infos.Add(new Info() { Id = 3, Name = "Info3" });
  29:       infos.Add(new Info() { Id = 1, Name = "Info1-A" });
  30:       infos.Add(new Info() { Id = 1, Name = "Info1-B" });
  31:    }  
  32:  
  33:    public List<Info> GetInfo(int value)
  34:    {
  35:       var query = from inf in this.infos where inf.Id == value select inf;
  36:       return query.ToList();
  37:    }   
  38: }

Modifichiamo il file web.config affinchè il servizio venga correttamente gestito da IIS:

   1: <configuration>
   2:     <system.web>
   3:         <compilation debug="true" targetFramework="4.0"/>
   4:     </system.web>
   5:     <system.webServer>
   6:         <modules runAllManagedModulesForAllRequests="true"/>
   7:     </system.webServer>
   8:     <system.serviceModel>
   9:         <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
  10:         <behaviors>
  11:             <serviceBehaviors>
  12:                 <behavior name="ServiceBehavior">
  13:                     <serviceMetadata httpGetEnabled="true"/>
  14:                     <serviceDebug includeExceptionDetailInFaults="true"/>
  15:                 </behavior>
  16:             </serviceBehaviors>
  17:         </behaviors>
  18:       <bindings>
  19:          <netTcpBinding>
  20:             <binding name="tcp">
  21:                <security mode="None"/>
  22:             </binding>
  23:          </netTcpBinding>
  24:       </bindings>
  25:         <services>
  26:             <service name="Service" behaviorConfiguration="ServiceBehavior">                
  27:                 <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcp" contract="IMyService"/>
  28:                 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  29:             </service>
  30:         </services>
  31:     </system.serviceModel>
  32: </configuration>

Configurazione di IIS

Do per scontato che WAS e relativo supporto al protocollo TCP-IP siano attivi, se non siete certi di questo guardate qui:http://bit.ly/4qka8T, affinchè IIS possa gestire correttamente le chiamate Tcp-Ip è necessario eseguire inoltre le seguenti operazioni:

1) Assicurarsi che il default site supporti il protocollo net.tcp sulla porta che desideriamo utilizzare che, come richiesto da Silverlight, deve essere nel range #4502-4534, nel nostro caso useremo la porta 4502 quindi il default site va configurato in questo modo:

image

2)Cambiare l’application pool e aggiungere il supporto al protocollo Tcp-Ip al sito che ospita il servizio WCF:

image

Creazione dell’applicazione Silverlight 4.0

Aggiungere alla soluzione un progetto Silverlight 4.0, per semplicità faremo ospitare l’applicazione alla stessa applicazione ASP.NET che espone il servizio WCF ma nulla impedisce di usare un sito web separato:

image

ora aggiungiamo un riferimento al servizio WCF esposto dal sito creato in precedenza, usando Add Service Refence dall’applicazione Silverlight 4.0:

image

se analizzate il file ServiceReference.ClientConfig noterete che l’endpoint utilizza un custom binding composto da binaryMessageEncoding e tcpTransport

   1: <configuration>
   2:     <system.serviceModel>
   3:         <bindings>
   4:             <customBinding>
   5:                 <binding name="NetTcpBinding_IMyService">
   6:                     <binaryMessageEncoding />
   7:                     <tcpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
   8:                 </binding>
   9:             </customBinding>
  10:         </bindings>
  11:         <client>
  12:             <endpoint address="net.tcp://d830:4502/SL4TcpBindingService/Service.svc"
  13:                 binding="customBinding" bindingConfiguration="NetTcpBinding_IMyService"
  14:                 contract="localhost.IMyService" name="NetTcpBinding_IMyService" />
  15:         </client>
  16:     </system.serviceModel>
  17: </configuration>

L’utilizzo del servizio è indipendente dalla modalità di binding e quindi è sufficiente scrivere:

   1: private void button1_Click(object sender, RoutedEventArgs e)
   2:       {
   3:          localhost.MyServiceClient proxy = new localhost.MyServiceClient();
   4:          proxy.GetInfoCompleted += (s, args) =>
   5:             {
   6:                if (args.Error == null)
   7:                {
   8:                   listBox1.ItemsSource = args.Result;
   9:                }
  10:  
  11:             };
  12:  
  13:          proxy.GetInfoAsync(1);
  14:       }

per ottenere l’elenco degli oggetti Info all’interno di una listbox.

In realtà se eseguite l’applicazione otterrete un eccezione che vi informa che il servizio non è accessibile:

image

il problema è causato dal fatto che quando si Silverlight comunica via Tcp-Ip contatta un Policy Server in ascolto sulla macchina di destinazione (localhost nel nostro caso) sulla porta 943, richiedendo la restituzione di un file ClientAccessPolicy.xml contenente le relative credenziali di accesso (more info here: http://bit.ly/7fpMhi).

Creazione del Policy Server

Creare un policy server non è un operazione banale, sopratutto perchè tutta la comunicazione avviene attraverso Sockets, fortunatamente nella gallery in Visual Studio 2010 è presente un template pronto all’uso.
Selezionate: Tools->Extension Manager->Online gallery e nel box di ricerca digitate “Silverlight tcp”, otterrete il template mostrato nella figura che segue, aggiungetelo ai templates di Visual Studio 2010:

image

a questo punto aggiungete alla vostra soluzione un progetto Silverlight TCP Socket Policy Server

image

e assicuratevi che venga lanciato insieme all’applicazione Silverlight modificando le opzioni di avvio della soluzione:

image

Premete F5 e ora avrete accesso al servizio via Tcp-Ip.

Ovviamente questa soluzione implica la possibilità di accedere attraverso la porta 4502 quindi è probabilmente più adatto ad un contesto Intranet, in qualsiasi caso nessuno vi impedisce di esporre il servizio su più EndPoints come mostrato di seguito:

   1: <services>
   2:             <service name="Service" behaviorConfiguration="ServiceBehavior">                
   3:                 <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcp" contract="IMyService"/>
   4:             <endpoint address="" binding="basicHttpBinding" contract="IMyService"/>
   5:                 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
   6:             </service>
   7:         </services>

 

ottenendo quindi un ServiceReference.ClientConfig che contiene entrambi gli endpoints:

   1: <client>
   2:             <endpoint address="net.tcp://d830:4502/SL4TcpBindingService/Service.svc"
   3:                 binding="customBinding" bindingConfiguration="NetTcpBinding_IMyService"
   4:                 contract="localhost.IMyService" name="NetTcpBinding_IMyService" />
   5:             <endpoint address="http://d830/SL4TcpBindingService/Service.svc"
   6:                 binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyService"
   7:                 contract="localhost.IMyService" name="BasicHttpBinding_IMyService" />
   8:         </client>

permettendovi perciò di decidere quale protocollo utilizzare.

Happy Tcp-Ip’ing smile_wink