WCF service na voľnom TCP porte

Nedávno som robil jednoduchú desktopovú aplikáciu, ktorá prenáša dáta medzi užívateľmi. Niečo ako instant messaging, len sa neprenášajú užívateľom písané správy. Keďže aplikácia nemusí fungovať cez internet, stačí len na LAN, tak som sa rozhodol použiť WCF a NetTcpBinding.

A keďže je to desktopová aplikácia a môj protokol nie je žiaden štandard, tak som nevyhradil žiaden špecifický TCP port, na ktorom by služba počúvala. Ale narazil som na problém, že donútiť WCF službu počúvať na ľubovoľnom voľnom porte a ešte zistiť ten port nie je celkom jednoduché. Našťastie je to možné a nie je to žiadna tragédia.

Najprv na NetTcpBinding musíme nastaviť nejaký špecifický namespace aj name. Potom po pridaní ServicePoint do ServiceHost nastavíme na ServicePoint ListenUriMode na Unique. Toto zabezpečí, že WCF služba bude fungovať na ľubovoľnom voľnom TCP porte.

A po spustení ServiceHost vyhľadáme ChannelDispatcher s príslušným name a namespace. A potom už stačí len zobrať Lister Uri a z nej prečítať port.

Tu je kód ako to spraviť.

// vytvorim novy ServiceHost

var host = new ServiceHost(new MyService(), new Uri(“net.tcp://localhost/MyService”));

// nastavim TCP binding

var binding = new NetTcpBinding();

binding.Namespace = “MyNamespace”;

binding.Name = “MyServiceBinding”;

// pridam a nastavim ServicePoint

var sp = host.AddServicePoint(typeof(IMyService), binding, String.Empty);

sp.ListenUriMode = ListenUriMode.Unique;

// spustime sluzbu

host.Open();

// zistim TCP post

var channelDispatcher = host.ChannelDispatchers.OfType<ChannelDispatcher>().FirstOrDefault(cd => cd.BindingName == “MyNamespace:MyServiceBinding”);

int port = 0;

if (channelDispatcher != null)

{

  port = channelDispatcher.Listener.Uri.Port;

}

Alebo ak máte radšej zápis pre konfiguračný súbor.

<system.serviceModel>

  <services>

    <service name=”MyService”>

      <host>

        <baseAddresses>

          <baseAddress baseAddress=”net.tcp://localhost/MyService”/>

        </baseAddresses>

      </host>

      <endpoint binding=”netTcpBinding” bindingNamespace=”MyNamespace” bindingName=”MyServiceBinding” listenUriMode=”Unique” />

    </service>

  </services>

</system.serviceModel>

Takto máme službu, ktorá sa vždy otvorí na náhodnom porte. Predpokladajme, že sa nám podarí toto číslo portu dostať na klienta. Ja som na to použil NetPeerTcpBinding kanál. Celkom sranda vecička, ale o tom inokedy. Keďže klient vie port pre daný počítač, tak nie je problém poskladať URI a pripojiť sa na WCF službu. No na moje prekvapenie to trochu problém je. Je totiž rozdiel medzi URI, ktorá identifikuje službu, a URI, na ktorej služba fyzicky beží.

URI, ktorá identifikuje službu, je tá, ktorú zadávame pri vytváraní endpointu (alebo servicepointu). V našom prípade je to base address, ktorú sme zadali pri vytvorení ServiceHost.

Fyzická URI je tá, ktorú musíme zadať klientovi, aby vedel, kam sa má pripojiť. V našom prípade to bude URI so správnym názvom počítača a uvedeným TCP portom. Napr.: net.tcp://duracellkopc:12345/MyService

A tu je kameň úrazu. WCF štandardne predpokladá, že obidve URI sú rovnaké. Teda až na názov počítača. To je jediná časť URI, ktorá sa ignoruje, avšak aj toto sa dá zmeniť. Takže klient vytvorí TCP spojenie a pošle požiadavku na službu so špecifickou URI. Lenže WCF na serverovej strane povie, že žiadnu službu na URI s takým portom nepozná.

Našťastie aj pri klientovi sa dá povedať, že má pristupovať k službe s určitou URI, ale fyzicky sa má pripojiť niekam inam. A to niekam inam sa vo WCF nazýva ClientViaBehavior. Čiže konštruktor pre klienta by bol asi takýto.

public MyServiceClient(Uri remoteAddress)

  : base(new NetTcpBinding(), GetPortLessUri(remoteAddress))

{

  Endpoint.Behaviors.Add(new ClientViaBehavior(remoteAddress));

}

 

private static Uri GetPortLessUri(Uri uri)

{

  var uriBuilder = new UriBuilder(uri);

  uriBuilder.Port = –1;

  return uriBuilder.Uri;

}

Tak a máme službu, ktorá beží na ľubovoľnom voľnom porte a zároveň aj klienta, ktorý sa na ňu vie pripojiť.

Zaradené do: ,

Komentáre

Bez komentárov

Prihlásiť | Registrovať | Pomoc