Nová verze WCF nabízí novou a velice dobře vypadající novinku “Discovery”. Tato novinka slouží pro hledání služeb, kde pro hledání využívá WS-Discovery protokol. WCF nabízí 2 módy pro hledání služeb:

  1. AD Hoc mód – funguje pouze v lokálním subnetu
  2. Managed mód – NENÍ omezen pouze na lokální subnet

Implementace druhého módu, je o něco málo komplikovanější a proto mu budu věnovat samostatný článek. Nyní si tedy ukážeme implementaci AD hoc módu.

Služba

Aby uměla naše služba reagovat na hledání ze strany klienta, musí mít endpoint UdpDiscoveryEndpoint (Klient, který provádí hledání má rovněž tento endpoint) na který přichází požadavky od klienta, tedy klient pošle zprávu na všechny služby, které mají uveden tento endpoint. Kromě tohoto endpointu je nutné v ServiceBehaviors “zapnout” viditelnost služby na síti a to pomocí elementu serviceDiscovery. Pokud by naše služba neměla uvedený element ServiceDiscovery, zprávy zaslané klientem by na takovou službu nepřišly.

   1: <system.serviceModel>
   2:     <services>
   3:       <service name="GreetingService.GreetingService">
   4:         <endpoint address=""
   5:                   binding="basicHttpBinding"
   6:                   contract="GreetingService.IHello"/>
   7:         <endpoint name="udpDiscoveryEndpoint" kind="udpDiscoveryEndpoint"/>
   8:       </service>
   9:     </services>
  10:     <behaviors>
  11:       <serviceBehaviors>
  12:         <behavior>
  13:           <serviceDiscovery />
  14:         </behavior>
  15:       </serviceBehaviors>
  16:     </behaviors>
  17:   </system.serviceModel>

Poznámka: V ukázce využívám službu, jejíž definice je následující:

   1: [ServiceContract]
   2: public interface IHello
   3: {
   4:     [OperationContract]
   5:     string SayHello(string str);
   6: }
   7:  
   8: [ServiceContract]
   9: public interface IGoodbye
  10: {
  11:     [OperationContract]
  12:     string SayGoodBye(string str);
  13: }
  14:  
  15: public class GreetingService : IHello, IGoodbye
  16: {
  17:     public string SayGoodBye(string str)
  18:     {
  19:         return string.Format("Goodbye {0}", str);
  20:     }
  21:  
  22:     public string SayHello(string str)
  23:     {
  24:         return string.Format("Hello {0}", str);
  25:     }
  26: }

Pro praktickou ukázku jsem si vytvořil chatovací aplikaci:

image

Pokud uživatel vyplní jméno a stiskne tlačítko “Sign In” vyvolá se následující metoda (důležité informace jsou v komentářích) Nedělá se zde nic zásadního, pouze se pomocí objektu ServiceHost spustí službu na adrese, která se automaticky vygeneruje. Konfigurační soubor, který jsem ukazoval o pár řádků výše je samozřejmě součástí této ukázky, bez něho by naše služba nebyla viditelná v síti a neuměla zpracovávat zpráva zaslané klientem:

   1: private void btnSignIn_Click(object sender, EventArgs e)
   2: {
   3:     if (string.IsNullOrWhiteSpace(txtUsername.Text))
   4:     {
   5:         MessageBox.Show("Username");
   6:         return;
   7:     }
   8:  
   9:     // vytvorim jedinecnou adresu, na ktere mi pobezi sluzba
  10:     localAddress = new EndpointAddress("http://localhost:8091/" + Guid.NewGuid().ToString());
  11:  
  12:     // objektu serviceHost predam do konstruktoru typ moji sluzbu, kterou chci hostovat a adresu na ktere bude dostupna
  13:     serviceHost = new ServiceHost(typeof(GreetingService.GreetingService), localAddress.Uri);
  14:     // asynchrone tuto sluzbu spustim
  15:     serviceHost.BeginOpen((result) => serviceHost.EndOpen(result), null);
  16:  
  17:     btnSignIn.Enabled = false;
  18:     btnSignOut.Enabled = true;
  19:     btnDiscovery.Enabled = true;
  20:     ChangeTitle(txtUsername.Text);
  21: }

Nyní je naše služba spuštěna a zároveň viditelná na síti. Ted se podíváme jaké máme možnosti na straně klienta, který provádí hledání takto viditelných služeb.

Klient

Pro implementaci klienta existuje nová třída nazvaná DiscoveryClient. Když vytváříme instanci této třídy, do konstruktoru uvádíme objekt typu UdpDiscoveryEndpoint aby ten klient veděl na jaké endpointy má zasílat zprávy při hledání. Dále tato nová třída obsahuje dvě užitečné události:

  • FindProgressChanged – tato událost se vyvolá, vždy když klient nalezne nějakou službu. Při odchycení této služby získáme z argumentů objekt EndpointDiscoveryMetadata, který obsahuje informace o nalezené službě
  • FindCompleted – jakmile klient dokončí hledání služeb

Ještě před samotným voláním metody Find nebo FindAsync musíme uvést nějaké kritéria, podle kterých se bude hledat. Pro definici kritérií existuje opět nová třída FindCriteria, kde do konstrukturou uvedu typ Service Contractu (řeknu, že hledám pouze ty služby, které nabízí tenhle ServiceContract). Nakonec tedy provedu volání metody Find či FindAsync kde jako parametr předám právě vytvořené kritéria

   1: private void ADHocMode()
   2: {
   3:     // vytvorim clienta
   4:     DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
   5:     discoveryClient.FindProgressChanged += (sender, args) =>
   6:         PopulateUsersList(args.EndpointDiscoveryMetadata);
   7:     discoveryClient.FindCompleted += (sender, args) =>
   8:         {
   9:             btnDiscovery.Enabled = true;
  10:             btnDiscovery.Text = "Discovery Users";
  11:         };
  12:  
  13:     // zadam podle jakych kriterii se bude hledat
  14:     FindCriteria findCriteria = new FindCriteria(typeof(GreetingService.IHello));
  15:  
  16:     // budu hledat asynchronne
  17:     discoveryClient.FindAsync(findCriteria);
  18:     btnDiscovery.Enabled = false;
  19:     btnDiscovery.Text = "Discovering users...";
  20: }

V obsluze události FindProgressChanged si můžete všimnout, že volám metodu PopulateUsersList, kde ovšem nedělám nic jiného, než že si z vlastnosti Address.Uri ziskám adresu nalezené služby a zobrazím v ListBoxu. Výsledek po hledání by mohl vypadat takto:

image

Poznámka: Adresa kterou zde vidíte je adresa jiného uživatele.