Programowanie Systemów Rozproszonych
Laboratorium 3
WCF
Paweł Paduch
paduch@tu.kielce.pl
12-04-2016
Rozdział 1
Wstęp
1.1
Materiały pomocniczne
Do zajęć przydatne mogą być:
• materiały udostępniane przez prowadzących wykłady
• książki związane z tematyką programowania np. „Język C# 2010 i
plat-forma .NET“Andrew Troelsena
• książki związane z tematyką programowania współbieżnego np.
„Progra-mowanie równoległe i asynchroniczne w C# 5.0”
• kursy i poradniki w sieci np. http://www.centrumxp.pl/
• Tutoriale Microsoftu np.https://msdn.microsoft.com/en-us/library/ms731064(v=vs.110).aspx
Rozdział 2
Komunikacja
dwukierunkowa - kontrakty
Po zalogowaniu się z menu start odnajdujemy program Microsoft Visual
Stu-dio 2010, lub nowszy.
2.1
Service Contract
Z górnego menu wybieramy: File-> New Project. W okienku jak na rysunku
Rysunek 2.1: Okienko wybierania nowego projektu
2.1zaznaczamy Visual C#->Windows WCF Service Library. Na dole wpisujemy
nazwę projektu DuplexServiceLibrary i nazwę solucji Lab3, potwierdzamy OK. Powinny powstać nam 3 pliki:
• Service.cs • App.config
Zmieniamy nazwę IService1.cs na IDuplexCalc.cs. W interfejsie definiujemy jakie operacje będzie udostępniać nasz serwis, (dodawanie, odejmowanie, mno-żenie, dzielenie) ale też definiujemy jakie operacje może wywoływac u klienta. Będą to odpowiednio Wyświetlenie wyniku i wyświetlenie równania.
Listing 2.1: IDuplexCalc.cs 1 usingSystem; 2 usingSystem.Collections.Generic; 3 usingSystem.Linq; 4 usingSystem.Runtime.Serialization; 5 usingSystem.ServiceModel; 6 usingSystem.Text; 7 8 namespaceDuplexServiceLibrary 9 { 10 [ServiceContract(SessionMode = SessionMode.Required, 11 CallbackContract = typeof(IDuplexCalcCallback))] 12 public interfaceIDuplexCalc {
13 [OperationContract(IsOneWay = true)] 14 void Wyczysc();
15 [OperationContract(IsOneWay = true)] 16 void DodajDo(double n);
17 [OperationContract(IsOneWay = true)] 18 void OdejmijOd(double n);
19 [OperationContract(IsOneWay = true)] 20 void PomnozPrzez(doublen); 21 [OperationContract(IsOneWay = true)] 22 void PodzielPrzez(double n); 23 }
24
25 public interfaceIDuplexCalcCallback 26 {
27 [OperationContract(IsOneWay = true)] 28 void Wynik(double result);
29 [OperationContract(IsOneWay = true)] 30 void Rownanie(string eqn);
31 } 32 }
2.2
Implementacja Kontraktu
Zmieniamy nazwę pliku Service1.cs na DuplexCalc.cs
Ustawiamy dziedziczenie DuplexCalc po IDuplexCalc. Można posłużyć się rozwi-janym menu by wygenerować automatycznie odpowiednie metody. Implemen-tujemy poszczególne metody jednocześnie implementując właściwość Calback zwracającą kanał zwrotny typu IDuplexCalcCallback
Listing 2.2: Zaimplementowane metody w DuplexCalc.cs
1 usingSystem; 2 usingSystem.Collections.Generic; 3 usingSystem.Linq; 4 usingSystem.Runtime.Serialization; 5 usingSystem.ServiceModel; 6 usingSystem.Text; 7 8 namespaceDuplexServiceLibrary
9 {
10 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
←-ConcurrencyMode = ←-ConcurrencyMode.Single)]
11 public classDuplexCalc : IDuplexCalc 12 { 13 14 double wynik = 0.0D; 15 string rownanie; 16 17 public DuplexCalc() 18 { 19 rownanie = wynik.ToString(); 20 } 21
22 public void Wyczysc()
23 {
24 Callback.Rownanie(rownanie +" = " + wynik.ToString()); 25 rownanie = wynik.ToString();
26 }
27
28 public void DodajDo(double n)
29 { 30 wynik += n; 31 rownanie += " + "+ n.ToString(); 32 Callback.Wynik(wynik); 33 } 34
35 public void OdejmijOd(double n)
36 { 37 wynik -= n; 38 rownanie += " - "+ n.ToString(); 39 Callback.Wynik(wynik); 40 } 41
42 public void PomnozPrzez(double n)
43 { 44 wynik *= n; 45 rownanie += " * "+ n.ToString(); 46 Callback.Wynik(wynik); 47 } 48
49 public void PodzielPrzez(double n)
50 { 51 wynik /= n; 52 rownanie += " / "+ n.ToString(); 53 Callback.Wynik(wynik); 54 } 55 56 IDuplexCalcCallback Callback 57 { 58 get 59 { 60 return OperationContext.Current.GetCallbackChannel<←-IDuplexCalcCallback>(); 61 } 62 } 63 64 } 65 }
2.3
App.config
Należy, zmienić nazewnictwo w pliku App.config z Service1 na
DuplexCal-culator oraz IService1 na IDuplexCalc. Zmienić też typ bindingu z BasicHttp-Binding na wsDualHttpBinding. Dodać sekcje bindings z definicją
Listing 2.3: App.config
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration>
3
4 <system.web>
5 <compilation debug="true" /> 6 </system.web>
7 <!-- When deploying the service library project, the content of the config file
←-must be added to the host's
8 app.config file. System.Configuration does not support config files for libraries.
←--->
9 <system.serviceModel> 10 <services>
11 <service name="DuplexServiceLibrary.DuplexCalc">
12 <endpoint address="" binding="wsDualHttpBinding"
contract="←-DuplexServiceLibrary.IDuplexCalc" bindingConfiguration="DuplexBinding">
13 <identity>
14 <dns value="localhost" /> 15 </identity>
16 </endpoint>
17 <endpoint address="mex" binding="mexHttpBinding"
contract="IMetadataExchange"←-/> 18 <host> 19 <baseAddresses> 20 <add baseAddress="http://localhost:8733←-/Design_Time_Addresses/DuplexServiceLibrary/DuplexCalculator/" /> 21 </baseAddresses> 22 </host> 23 </service> 24 </services> 25 <bindings> 26 <wsDualHttpBinding>
27 <binding name="DuplexBinding"
clientBaseAddress="http://localhost:8000←-/DuplexCalc/"/> 28 </wsDualHttpBinding> 29 </bindings> 30 <behaviors> 31 <serviceBehaviors> 32 <behavior>
33 <!-- To avoid disclosing metadata information, 34 set the value below to false before deployment --> 35 <serviceMetadata httpGetEnabled="True"/>
36 <!-- To receive exception details in faults for debugging purposes, 37 set the value below to true. Set to false before deployment 38 to avoid disclosing exception information -->
39 <serviceDebug includeExceptionDetailInFaults="true" /> 40 </behavior> 41 </serviceBehaviors> 42 </behaviors> 43 </system.serviceModel> 44 45 </configuration>
2.4
Pierwsze uruchomienie
Już teraz można uruchomić nasz serwis wciskając F5. Serwis będzie hosto-wany w środowisku uruchomieniowym, włączony zostanie też prosty klient do
testowania rozwiązań (rys. 2.2).
2.5
Hostowanie serwisu
Stworzymy sobie prostą aplikację konsolową hostującą nasz serwis.
1. Dodaj do solucji nowy projekt typu Console Application i nazwij ją
Rysunek 2.2: WCF Test Client
2. We właściwościach projektu zmień Target framework na .NET Framework
4.5 (rys.2.3). Projekt zostanie przeładowany.
3. Dodaj referencję do DuplexServiceLibrary. W solution explorerze prawym klawiszem myszy na klikamy na References i wybieramy Add reference. Wybieramy z menu z lewej strony Solution -> Projects i zaznaczamy
Du-plexServiceLibrary(rys.2.4). Dzięki temu aplikacja będzie znała typy
uży-wane w projekcie serwisu.
4. Dodaj referencję do System.ServiceModel. Podobnie jak wyżej, tylko na-leży wybrać nie Solution a Assemblies->Framework i tam zaznaczyć
Sys-tem.ServiceModel.
Uzupełnij kod w pliku Program.cs jak na listingu2.4
Listing 2.4: Główny program hostujący serwis
1 usingSystem; 2 usingSystem.Collections.Generic; 3 usingSystem.Linq; 4 usingSystem.ServiceModel; 5 usingSystem.ServiceModel.Description; 6 usingSystem.Text; 7 usingDuplexServiceLibrary; 8 9 namespaceDuplexServiceHost 10 { 11 classProgram 12 {
13 static void Main(string[] args)
14 {
15 // Tworzymy adres pod którym będzie dostępna usługa
16 Uri baseAddress = new Uri("http://localhost:8001/DuplexCalcService/"); 17
18 // Tworzymy obiekt klasy DuplexCalc
19 ServiceHost selfHost = newServiceHost(typeof(DuplexCalc), baseAddress); 20
21 try
22 {
23 // Dodajemy Endopoint usługi 24
25 selfHost.AddServiceEndpoint(typeof(IDuplexCalc), new
Rysunek 2.3: Właściwości projektu
26
27 // Umożliwiamy wymianę metadanych
28 ServiceMetadataBehavior smb =newServiceMetadataBehavior(); 29 smb.HttpGetEnabled =true;
30 // Umożliwiamy przesłanie dodatkowych danych na temat wyjątku 31 selfHost.Description.Behaviors.Find<ServiceDebugBehavior>().←-IncludeExceptionDetailInFaults = true; 32 selfHost.Description.Behaviors.Add(smb); 33 34 35 // Startujemy serwis 36 selfHost.Open();
37 Console.WriteLine("Serwis działa....");
38 Console.WriteLine("Naciśnij <ENTER> by zakończyć."); 39 Console.WriteLine(); 40 Console.ReadLine(); 41 42 // zamykamy serwis 43 selfHost.Close(); 44 }
45 catch (CommunicationException ce)
46 {
47 Console.WriteLine("Przechwyciłem wyjątek: {0}", ce.Message); 48 selfHost.Abort();
49 }
50 }
51 } 52 }
W linii 25 tworzymy Endpoint serwisu. Jest on złożony z adresu, bindingu oraz kontraktów.
Kontrakt to IDuplexCalc. Binding to WSDualHttpBinding, który jest predefi-niowanym bindingiem, umożliwia on komunikację dwustronną. Od wersji .Net 4.0 podawanie bindingu nie jest wymagane. Zostanie on automatycznie wyge-nerowany dla wszystkich par bazowy adres i kontrakt.
W linii 28 umożliwiamy wymianę metadanych serwisu. Klienci używają meta-danych do wygenerowania proxy służących do komunikowania się z serwisem. Należy:
• stworzyć obiekt klasy ServiceMetadataBehavior • ustawić jego parametr HttpGetEnabled na true
• dodać „zachowanie” (behavior) do kolekcji System.ServiceModel.ServiceHost.Behaviors. W lini 31 umożliwiamy przesyłanie błędów z serwisu do klienta.
W linii 36 otwieramy host do nasłuchiwania przychodzących komunikatów. Ustawiamy projekt DuplexServiceHost jako StartUp Project i uruchamiamy.
Możemy uruchomić przeglądarkę i wejść na adres: http://localhost:8001/DuplexCalcService/. Powinniśmy zobaczyć linki do plików WSDL oraz przykład prostego klienta.
2.6
Klient
Klient korzysta z klas proxy wygenerowanych na podstawie metadanych. Metadane często są w postaci pliku WSDL (plik xmlowy opisujący web service). Klasy proxy można wygenerować za pomocą narzędzia Svcutil.exe dostępnego z konsoli Visual Studio Command Prompt lub z poziomu Visual Studio.
1. Dodaj nowy projekt typu aplikacja konsolowa o nazwie
DuplexCalcPro-gram do solucji Lab3.
2. Ustaw we właściwościach projektu Target Framework na .Net 4.5 3. Dodaj referencje do System.ServiceModel
4. Dodaj dodaj referencje do serwisu. W tym celu należy:
• Uruchomić program CalculatorHost tak by serwis działał i wystawił metadane na adresie http://localhost:8001/DuplexCalcService/. Na-leży uruchomić w trybie bez debugowania (ctrl+F5).
• Prawym klawiszem myszy kliknąć w solution explorerze na References w projekcie DuplexCalcProgram i wybrać Add Service Reference. • podać adres serwisu i nacisnąć guzik Go.
• po odkryciu naszego serwisu podajemy nazwę przestrzeni nazw
Du-plexCalcServiceRef (rys.2.6) i naciskamy OK.
Rysunek 2.6: Dodaj referencje do serwisu
• zostanie wygenerowany kod do klas proxy oraz plik konfiguracyjny
app.config.
5. Jeżeli chcielibyśmy użyc narzędzia Svcutil.exe należałoby podać mniej wię-cej taką komendę:
svcutil.exe /language:cs /out:generatedProxy.cs /config:app.config http://localhost:8001/DuplexCalcService/
6. W kodzie musimy stworzyć klasę implementującą wywołania serwisu na kliencie CallbackHandler. Są tam dwie metody Wynik i Rownanie. Serwis za każdym razem gdy wołamy metodę liczacą, wywołuje u klienta metodę zwracająca wynik.
7. Uzupełniamy kod programu o odpowiednią przestrzeń nazw oraz tworzy-my obiekt klasy DuplexCalcClient wołatworzy-my metody operujące na
kalkula-torze np DodajDo czy PomnozPrzez jak listeningu (2.5)
Listing 2.5: Calculator Client
1 usingSystem; 2 usingSystem.Collections.Generic; 3 usingSystem.Linq; 4 usingSystem.ServiceModel; 5 usingSystem.Text; 6 usingDuplexCalcProgram.DuplexCalcServiceRef; 7 8 namespaceDuplexCalcProgram 9 { 10
11 public classCallbackHandler : IDuplexCalcCallback 12 {
13 public void Wynik(double result)
14 {
15 Console.WriteLine("Wynik({0})", result);
16 }
17
18 public void Rownanie(string eqn)
19 {
20 Console.WriteLine("Równanie({0})", eqn);
21 }
22 }
23 classProgram 24 {
25 static void Main(string[] args)
26 {
27
28 InstanceContext instanceContext = newInstanceContext(new
CallbackHandler←-());
29
30 DuplexCalcClient client = newDuplexCalcClient(instanceContext); 31 client.Open(); 32 client.DodajDo(5.0); 33 client.DodajDo(3.0); 34 client.PomnozPrzez(2.0); 35 client.PodzielPrzez(4.0); 36 client.OdejmijOd(1); 37 client.Wyczysc(); 38 Console.ReadLine(); 39 40 client.Close(); 41 } 42 } 43 }
Rozdział 3
Zadania do samodzielnego
wykonania
Dodać do Callbacku wątek, który co sekundę woła metodę u klienta
(licznik-Stan), która przekaże mu stan licznika. Licznik powinien startować od pierwszej
operacji wykonanej na serwisie i zatrzymywać się za pomocą innej zdalnej me-tody np licznikStop.