Spis treści:

Kategoria:C#Windows Communication Foundation


Przygoda z WCF, prosty klient

W poprzednim wpisie, mam nadzieję, szczegółowo opisałem tworzenie prostego serwera WCF. Nadszedł czas na skorzystanie z metody udostępnianej przez ten właśnie serwer. Przypomnijmy sobie, że udostępniana jest metoda Hello, która zwraca tekst powitalny.

Od czego zacząć

Najlepiej otworzyć projekt serwera, bo będzie on nam na tym etapie mocno potrzebny. Przypomnijmy sobie jeszcze, że tworząc aplikację konsolową serwera definiowaliśmy pewien interfejs WCF z jedną metodą. Ten interfejs jest w rzeczywistości kontraktem, swojego rodzaju umową, która precyzyjnie i jasno określa, w jaki sposób będzie się odbywała komunikacja pomiędzy klientem a serwerem.

Stwórzmy zatem nowy projekt aplikacji konsolowej, czyli naszego klienta WCF. Pierwszą czynnością pozwalającą na komunikację powinno być dodanie definicji interfejsu do tego projektu. Ważne jest to, aby definicje interfejsów po stronie klienta i po stronie serwera były identyczne. Muszą się zgadzać nazwy, przestrzeń nazw, parametry. Najlepiej jest po prostu ten plik skopiować z gotowego projektu serwera.

Istnieją znacznie lepsze metody, które powinno się stosować. Rozwiązanie przyjęte na tym etapie ma tę zaletę, że jest wyjątkowo proste. Wada jest taka, że istnieją teraz dwa identyczne pliki, które muszą być gdzieś przechowywane. Co więcej - jakakolwiek zmiana w jednym z tych plików wymusza identyczną zmianę w drugim pliku. Przy małym projekcie i jednym interfejsie problem może się wydać błahy i trywialny. Znacznie lepszym rozwiązaniem jest dodanie referencji projektu serwerowego na kliencie. Rozwiązanie to sprawia, że nie dublujemy interfejsu, ale jako efekt uboczny umieszcza kod serwera na kliencie. Najlepszym rozwiązaniem wydaje się wydzielenie interfejsu, a w zasadzie wszystkich części wspólnych do jeszcze jednego projektu. Powstają w ten sposób projekty: pierwszy z definicją interfejsu, drugi z kodem serwera (musi mieć dodaną referencję do projektu z interfejsem), oraz trzeci z kodem klienta (także musi mieć dodaną referencję do projektu z interfejsem).

Przypomnijmy sobie zatem definicję interfejsu:

interface IHelloWorld
{
  string Hello();
}

W jaki sposób klient łączy się z serwerem

Sposób tworzenia klienta zależy w dużej mierze od sposobu defnicji serwera. Przyjrzyjmy się fragmentowi kodu znajdującemu się w metodzie main serwera. Dla większej przejrzystości mniej ważne fragmenty z punktu widzenia klienta zostały pominięte:

static void Main(string[] args)
{
  Uri adres = new Uri("http://localhost:2222/Hello");
  ...
  host.AddServiceEndpoint(typeof(IHelloWorld), new BasicHttpBinding(), adres);
  ...
}

Serwer nie działa w próżni. Aby mógł on odbierać komunikaty, musi nasłuchiwać na konkretnym adresie. Pod ten właśnie adres klient musi wysyłać zgłoszenia. Adres ma postać następującą: http://localhost:2222/Hello. Druga pozostawiona linijka kodu serwera definiuje, oprócz adresu, który jest w niej wykorzystany, sposób komunikacji. Generalnie rzecz ujmując jest to protokół komunikacyjny, czyli sposób kodowania informacji zawartej w komunikacie. Aby klient rozumiał serwer protokoły te muszą być jednakowe. BasicHttpBinding to podstawowy protokół HTTP wykorzystywany powszechnie w sieci Internet.

Protokołów jest znacznie więcej i zostaną one opisane w jednym z kolejnych artykułów. Co więcej, ten sam serwer może obsługiwać wiele różnych protokołów jednocześnie. To jeszcze bardziej komplikuje całe zagadnienie.

Mając na względzie powyższe wyjaśnienia przystąpmy do napisania kodu klienta.

Kod prostego klienta WCF

Zanim wyjaśnię poszczególne elementy układanki przyjrzyjmy się fragmentowi kodu zaprezentowanemu poniżej:

static void Main(string[] args)
{
  Uri adres = new Uri("http://localhost:2222/Hello");
  using (var c = new ChannelFactory<IHelloWorld>(
    new BasicHttpBinding(),
    new EndpointAddress(adres)))
  {
    var s = c.CreateChannel();
    Console.WriteLine(s.Hello());
    Console.ReadLine();
  }
}

Kod jest krótki i przejrzysty, ale aby w pełni zrozumieć zasadę działania należy się nad nim chwilę zatrzymać. Tworzenie adresu nie jest niespodzianką. Nowością może być sposób użycia typowanej klasy ChannelFactory. To jest jeden z kluczowych elementów całej układanki. Za pomocą tej klasy tworzy się kanał komunikacyjny, czyli inaczej mówiąc połączenie pomiędzy klientem a serwerem. Jak już zostało napisane, musi się zgadzać protokół (BasicHttpBinding) oraz adres (http://localhost:2222/Hello). Klasa ChannelFactory<IHelloWorld> jest implementacją wzorca fabryki klas typu IHelloWorld, a ściślej klasy pośredniczącej, która implementuje ten interfejs. Klasy te tworzone są za pomocą metody CreateChannel. Metoda zwraca obiekt pośredniczący, ale zrzutowany na wykorzystywany przez nas interfejs. Tym obiektem posługujemy się tak samo, jak każdym zwykłym interfejsem. Przykładem tego jest chociażby wywołanie metody Hello().

W rzeczywistości wykonywane jest połączenie sieciowe, a dane wymieniane są poprzez protokół HTTP. Co najistotniejsze, całość odbywa się poza naszą wiedzą i niejako automatycznie.

Po zakończeniu operacji pamięć i obiekty zajmowane przez fabrykę klas zostają automatycznie zwolnione dzięki instrukcji using.

Co dalej

W tym momencie należy zdać sobie sprawę z tego, że jest to ekstremalnie proste rozwiązanie. WCF ma znacznie większe możliwości, ale wymagają one znacznie większego nakładu pracy i są bardziej skomplikowane. Na tym etapie polecam drobne eksperymenty z programem. Jednym z ćwiczeń niech będzie dodanie drugiej metody do interfejsu, być może takiej, która przyjmuje jakieś parametry. Polecam sprawdzić co się dzieje, gdy uruchomimy dwa serwery. Co się stanie, gdy uruchomimy dwie aplikacje klienta. Zachęcam do dzielenia się spostrzeżeniami w komentarzu.

Kategoria:C#Windows Communication Foundation

, 2013-12-20

Komentarze:

aaa (2015-11-28 15:20:42)
dzieki wielkie
romigrabarz (2016-06-08 13:07:41)
Świetny wstęp do WCF. W końcu pisze to ktoś z "mózgiem" :)
Vanguardb52 (2017-02-23 10:17:54)
Ja łapie błąd EndpointNotFoundException w lini Console.WriteLine(s.Hello()),

Czy serwer jest uruchomiony czy nie. Ale to może wina FireWalla
Adam (2017-10-10 11:54:52)
U mnie wyrzuca wyjątek:

Exception thrown: 'System.InvalidOperationException' in System.ServiceModel.dll
An unhandled exception of type 'System.InvalidOperationException' occurred in System.ServiceModel.dll
Additional information: Próbowano uzyskać typ kontraktu dla IHelloWorld, ale ten typ nie jest typu ServiceContract, ani nie dziedziczy z klasy ServiceContract.

Jakieś wskazówki czemu tak się dzieje lub jak to naprawić?
PD (2017-10-18 12:50:50)
Najbardziej prawdopodobnym problemem jest brak atrybutu [ServiceContract] na interfejsie IHelloWorld. Jest to dokładniej opisane w artykule wskazanym we wstępie (tworzenie prostego serwera WCF).
Piotr (2018-01-20 10:38:13)
Fajny, super artykuł. Moim zdaniem bardzo dobrze napisany.
Odnośnie uruchomienia 2 serwerów jednocześnie powinno wyrzucić błędem ponieważ 2 serwery nie mogą nasłuchiwać na tym samym porcie równocześnie. Natomiast odpalenie 2 klientów spowoduje że w tym prostym przypadku będą działać niezależnie :) Tak myślę, nie sprawdzałem bo nie mam zainstalowanego VS na tym kompie

Czekam na więcej ciekawych wpisów.
Dodaj komentarz
Wyślij
Ostatnie komentarze
Dzieki za te informacje. były kluczowe
Dobrze wyjaśnione, dzięki !
a z innej strony - co gdybym ciąg znaków chciał mieć rozbity nie na wiersze a na kolumny? Czyli ciąg ABCD: 1. kolumna: A, 2. kolumna: B, 3. kolumna: C, 4 kolumna: D?
Ciekawy artykuł.
Czy można za pomocą EF wysłać swoje zapytanie?
Czy lepiej do tego użyć ADO.net i DataTable?