background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 1 

14. Komunikacja rozproszona  

– gniazdka, RMI 

 

14.1 Pakiet 

java.net

 

14.2 Mechanizm gniazdek TCP 

14.3 Przykład aplikacji klient-serwer 

14.4 RMI – model DOA 

14.5 Przykład aplikacji rozproszonej 

 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 2 

14.1 Pakiet java.net - wprowadzenie 

1) Elementy programowania sieciowego 

Aplikacje  programowe  w  Javie  korzystaj

ą

  z  protokółów  warstwy 

transportowej sieci:  



 „Transmission Control Protocol” (TCP) lub 



 „User Datagram Protocol” (UDP). 

 



 

TCP

 to poł

ą

czeniowy, niezawodny protokół typu point-to-point – dane 

nadchodz

ą

 w kolejno

ś

ci wysłania. 

Z  TCP  korzystaj

ą

  takie  aplikacje,  jak  Hypertext  Transfer  Protocol 

(HTTP), File Transfer Protocol (FTP), Telnet. 



 

UDP

 to bezpoł

ą

czeniowy protokół niezale

Ŝ

nego przesyłania pakietów 

danych (tzw. datagramy).  
Korzystaj

ą

  z  niego  programy  nie  wymagaj

ą

ce  gwarantowanych 

poł

ą

cze

ń

, np. serwer zegara, program ping.  

 

Porty

  -  logiczne  punkty  wej

ś

cia  do  aplikacji  pracuj

ą

cych  na  danym 

komputerze, doł

ą

czonym do sieci. 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 3 

Dane  przekazywane  przez  Internet  posiadaj

ą

  adres  swojego 

przeznaczenia: 



 adres komputera - 

32-bitowy adres IP

,  



 adres portu - 

16-bitowy numer portu

W komunikacji według protokółu TCP, serwer zwi

ą

zuje swoje 

gniazdko 

z okre

ś

lonym portem – rejestruje si

ę

 pod okre

ś

lonym portem. Równie

Ŝ

 

w  protokóle  UDP 

pakiety  kierowane  s

ą

  do  zadanego  portu, 

przydzielonego pewnej aplikacji. 

     

 

 

Numery portów: 

od 0 do 65535

 . 

Numery 

zastrze

Ŝ

one

 portów: 

od 0 do 1023

 – zarezerwowane na usługi 

takie, jak HTTP, FTP, itd. 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 4 

Klasy w 

java.net

 

Komunikacj

ę

  zgodn

ą

  z  tymi  protokółami  TCP  i  UDP  realizuj

ą

  klasy 

pakietu 

java.net



 

URL, URLConnection, Socket, ServerSocket

 (dla TCP); 



 

DatagramPacket, DatagramSocket, MulticastSocket

 

(dla UDP). 

  

2) Klasa 

URL

  

URL  (Uniform  Resource  Locator)  to  adres  zasobu  w  Internecie.  URL 
ma posta

ć

 napisu specyfikuj

ą

cego:  

 

protokół

 dla dost

ę

pu do zasobu i  

 

lokalizacj

ę

 zasobu.  

URL

  jest  te

Ŝ

  nazw

ą

  klasy  w 

java.net

.  Obiekt  klasy 

URL

  reprezentuje 

adres URL.  

Konstruktory klasy 

URL

 

1.

 

Z parametrem

 typu 

String

 reprezentuj

ą

cym 

adres bezwzgl

ę

dny.

 Np.: 

URL adresPW = new URL("http://www.pw.edu.pl/"); 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 5 

2. 

Z  dwoma  parametrami

  –  obiektem  URL  reprezentuj

ą

cym  adres 

bazowy

 i parametrem typu 

String

 reprezentuj

ą

cym 

adres wzgl

ę

dny

.  

Np.: dane s

ą

 dwa adresy 

URL

 

http://www.ia.pw.edu.pl/dydaktyka/proz.html 
http://www.ia.pw.edu.pl/dydaktyka/probe.html 

Mo

Ŝ

na  utworzy

ć

  obiekty  klasy  URL  dla  obu  stron  na  podstawie 

adresów wzgl

ę

dem jednej bazy - 

http://www.ia.pw.edu.pl/dydaktyka/ 

:  

URL dydaktyka = new URL("http://www.ia.pw.edu.pl/dydaktyka/"); 
URL dydaktykaProz = new URL(dydaktyka, "proz.html"); 
URL dydaktykaProbe = new URL(dydaktyka, "probe.html"); 

korzystaj

ą

c z konstruktora: 

URL(URL bazowyURL, String wzglednyURL

Mo

Ŝ

na te

Ŝ

 utworzy

ć

 

referencj

ę

 do pozycji w dokumencie

. Np. 

URL dydaktykaProzProjekt = new URL(dydaktykaProz, "#PROJEKT"); 

3. Trzy-argumentowy konstruktor URL: 

new URL("http", "www.ia.pw.edu.pl", "/dydaktyka/proz.html"); 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 6 

4.  Cztero-argumentowy  konstruktor  URL  uwzgl

ę

dniaj

ą

cy  numer  portu. 

Np.  

URL  dydaktykaProz  =  new  URL("http",  "www.ia.pw.edu.pl”,80, 
"dydaktyka/proz.html"); 

utworzy obiekt klasy 

URL

 dla nast

ę

puj

ą

cego adresu URL:  

http://www.ia.pw.edu.pl:80/dydaktyka/proz.html 

 

Ka

Ŝ

dy 

kontruktorów 

klasy 

URL

 

mo

Ŝ

zgłosi

ć

 

wyj

ą

tek

 

MalformedURLException

  (gdy  argumenty  referuj

ą

 

null

  lub  nieznany 

protokół).  

try 
    URL myURL = new URL(. . .) 
catch (MalformedURLException e) { 

    // obsługa wyjątku 

    . . . 

Obiekty klasy URL 

nie mog

ą

 by

ć

 zmieniane

 po pierwszej inicjalizacji. 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 7 

Podstawowe metody klasy URL  

 Metody zwracaj

ą

ce pełny adres URL dla danego obiektu klasy 

URL

String toString() 
String toExternalForm(). 

 Metody podaj

ą

ce informacje o stanie obiektu URL: 

getProtocol

 – podaje cz

ęść

 URL dotycz

ą

c

ą

 protokółu; 

getHost

 - podaje cz

ęść

 URL dotycz

ą

c

ą

 adresu hosta; 

getPort

 - podaje cz

ęść

 URL dotycz

ą

c

ą

 numeru portu (liczba integer lub 

warto

ść

  -1 gdy port nie jest ustawiony); 

getFile 

- podaje cz

ęść

 URL dotycz

ą

c

ą

 nazwy pliku; 

getRef

 - podaje cz

ęść

 URL dotycz

ą

c

ą

 referencji w pliku dokumentu. 

 

Przykład 14.1

. Tworzenie i korzystanie z obiektu klasy 

URL

import java.net.*; 
import java.io.*; 
 
public class ParseURL { 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 8 

    public static void main(String[] args) throws Exception { 
       URL aURL = new URL

("http://java.sun.com:80/docs/books/" 

                           + "tutorial/index.html#DOWNLOADING"

); 

        System.out.println("protocol = " + aURL.getProtocol()); 
        System.out.println("host = " + aURL.getHost()); 
        System.out.println("filename = " + aURL.getFile()); 
        System.out.println("port = " + aURL.getPort()); 
        System.out.println("ref = " + aURL.getRef()); 
    } 

Wynik pracy programu: 

protocol = http 
host = java.sun.com 
filename = /docs/books/tutorial/index.html 
port = 80 
ref = DOWNLOADING 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 9 

Metody klasy URL do poł

ą

cze

ń

 w sieci 

Metoda 

openStream()

  

Metoda  zwraca  obiekt  strumieniowy  klasy 

java.io.InputStream

 

zwi

ą

zany z adresem 

URL

.  

Przykład  14.2.

  Wykorzystanie  metody 

openStream

()

  do  poł

ą

czenia  w 

sieci. Odczyt informacji - poprzez obiekt klasy 

BufferedReader

 i zapis na 

standardowym wyj

ś

ciu. 

import java.net.*; 
import java.io.*; 
public class
 URLReader { 
    public static void main(String[] args) throws Exception { 
  URL yahoo new URL("http://www.yahoo.com/"); 
  BufferedReader in = new BufferedReader
   

 

 

new InputStreamReader

   

 

 

yahoo.openStream()));  

 

  String inputLine; 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 10 

  while ((inputLine = in.readLine()) != null) 
      System.out.println(inputLine); 
  in.close(); 
    } 

Metoda 

openConnection()

   

Metoda  zwraca  obiekt  klasy 

URLConnection

,  który  mo

Ŝ

e  by

ć

 

wykorzystany do szczegółowej współpracy z adresem URL. Np. 

try { 

URL yahoo = new URL("http://www.yahoo.com/"); 
URLConnection yahooConnection = yahoo.openConnection(); 

  } catch (MalformedURLException e) { 

// 

new URL() 

nie powiodło si

ę

 

 

    . . . 

  }  
  catch (IOException e) { 

// 

openConnection()

 nie powiodło si

ę

 

    . . . 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 11 

3) Metody klasy 

URLConnection 

 

Przykład 14.3.

 

Odczyt z URL

 za pomoc

ą

 obiektu klasy 

URLConnection

import java.net.*; 
import java.io.*; 
public class
 URLConnectionReader { 
    public static void main(String[] args) throws Exception { 
        URL yahoo = new URL("http://www.yahoo.com/"); 
        URLConnection yc = yahoo.openConnection(); 

// Poł

ą

cz 

        BufferedReader in = new BufferedReader
                                new InputStreamReader
                                yc.getInputStream())); 

// Pobierz strumie

ń

  

        String inputLine; 
        while ((inputLine = in.readLine()) != null)  
            System.out.println(inputLine); 
        in.close(); 
    } 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 12 

Adresy  URL  do  których  doł

ą

czono 

skrypty 

cgi-bin

  wymagaj

ą

,  aby 

zapisywa

ć

 informacj

ę

 do URL

.  

Typowy  przykład 

zapisu  do

 

URL

  polega  na  wypełnieniu  przez 

u

Ŝ

ytkownika zapytania podanego w postaci formatki na stronie HTML i 

wysłaniu  go  do  serwera  pod  adres  URL.  Przetwarza  on  zwykle  skrypt 

cgi-bin

 po stronie serwera i odpowiada w postaci strony HTML. 

Współpraca  programu  Javy  ze  skryptami  cgi-bin  po  stronie  serwera 
wymaga mo

Ŝ

liwo

ś

ci zapisu do 

URL

 przez ten program. Czyli wymaga 

to realizacji nast

ę

puj

ą

cych kroków w rogramie: 

1. 

Utworzy

ć

 obiekt typu 

URL

.  

2. 

Otworzy

ć

 poł

ą

czenie z tym 

URL

.  

3. 

Ustawi

ć

 mo

Ŝ

liwo

ś

ci wyj

ś

ciowe dla 

URLConnection

.  

4. 

Pobra

ć

  strumie

ń

  wyj

ś

ciowy  z  poł

ą

czenia  –  bedzie  on 

poł

ą

czony  ze  standardowym  strumieniem  wej

ś

ciowym 

skryptu 

cgi-bin

 po stronie serwera.   

5. 

Zapisywa

ć

 dane do strumienia wyj

ś

ciowego.  

6. 

Zamkn

ąć

 strumie

ń

.  

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 13 

Przykład  14.4.

  Skrypt  na  naszej  stronie  sieci  odczytuje  napis  ze 

standardowego  wej

ś

cia

,  odwraca  kolejno

ść

  znaków  napisu  i  zapisuje 

wynik d

o standardowego wyj

ś

cia

. Skrypt wymaga danych wej

ś

ciowych 

w formacie 

string=napis,

 gdzie napis jest napisem do odwrócenia.  

import java.io.*; 
import java.net.*; 
public class
 Reverse { 
    public static void main(String[] args) throws Exception { 
 

if (args.length != 1) { 

 

    System.err.println("Wywołanie: java Reverse " + "napis"); 

 

    System.exit(1); 

 

String stringToReverse = URLEncoder.encode(args[0]); 
URL url = new URL("http://java.sun.com/cgi-bin/backwards"); 
URLConnection connection = url.openConnection(); 
connection.setDoOutput(true);  

// UmoŜliwić wyjście do URL

 

PrintWriter out = new PrintWriter
                              connection.getOutputStream());  

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 14 

 

out.println("string=" + stringToReverse); 

 

out.close(); 

 

BufferedReader in = new BufferedReader

 

 

 

 

new InputStreamReader

 

 

 

 

connection.getInputStream())); 

 

String inputLine; 

 

while ((inputLine = in.readLine()) != null) 

 

    System.out.println(inputLine); 

 

in.close(); 

    } 

Komentarz do programu: 
1) Przetwarzanie parametrów programu z linii komend:  

if (args.length != 1) { 
    System.err.println("Wywołanie:  java Reverse " + "napis"); 
    System.exit(-1); 

String stringToReverse = URLEncoder.encode(args[0]); 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 15 

Ma  by

ć

  tylko  jeden  parametr  programu  i  kodowany  on  jest  dla 

przesłania (w tym spacje i znaki przestankowe) go do skryptu cgi-bin.  

2) Utworzenie obiektu klasy URL dla skryptu na 

java.sun.com

, otwarcie

 

URLConnection

 i ustawienie poł

ą

czenia do zapisu:  

URL url new URL("http://java.sun.com/cgi-bin/backwards"); 
URLConnection c = url.openConnection(); 
c.setDoOutput(true); 

// Ustawienie do zapisu

 

3)  Utworzenie  strumienia  wyj

ś

ciowego

  dla  poł

ą

czenia  i  utworzenie  na 

nim obiektu 

PrintWriter

 :  

PrintWriter out = new PrintWriter(c.getOutputStream()); 

Je

ś

li 

URL

  nie  zezwala  nam  na  zapis  (brak  wyj

ś

cia)  to  metoda 

getOutputStream

 zgłosi wyj

ą

tek 

UnknownServiceException

.  

4) Zapis danych do strumienia wyj

ś

ciowego i zamkni

ę

cie strumienia

:  

out.println("string=" + stringToReverse); 
out.close(); 

Strumien  wyj

ś

ciowy  klienta  jest  strumieniem  wej

ś

ciowym  serwera  – 

skryptu realizuj

ą

cego odrócenie kolejno

ś

ci liter.  

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 16 

5) Odczytujemy informacj

ę

 zwrotn

ą

 ze skryptu

BufferReader in = new BufferedReader
                  new InputStreamReader(c.getInputStream())); 
String inputLine; 
while ((inputLine = in.readLine()) != null) 
    System.out.println(inputLine); 
in.close(); 

Przykład wyniku działania: 

Reverse Me 
 reversed is:  
eM esreveR 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 17 

14.2 Mechanizm gniazdek TCP 

Do 

komunikacji 

sieciowej 

wykorzystaniem 

protokółu 

TCP

 

wykorzystywane s

ą

 mechanizmy: 



 strumieni  i  



 gniazdek (ang. 

socket

) - udost

ę

pniany w pakiecie 

java.net.*

.

 

Programista  mo

Ŝ

e  skupi

ć

  si

ę

  na  tym 

jakie  dane  chce  przesła

ć

  i  pod 

jaki adres 

albo 

co chce odebra

ć

 i sk

ą

d. 

1) Gniazdka sieciowe dla 

TCP: Socket i ServerSocket

 

Do 

bezpo

ś

redniego  komunikowania 

u

Ŝ

ywany  jest  obiekt  klasy 

Socket

a do 

prowadzenia nasłuchu

 - 

ServerSocket

Tworz

ą

c  gniazdko  do  komunikacji

  typu 

Socket

  podajemy  adres 

komputera, z którym chcemy si

ę

 poł

ą

czy

ć

 oraz numer portu, na którym 

ma zosta

ć

 nawi

ą

zane poł

ą

czenie.  

Np.: 

Socket s = new Socket("localhost", 4444); 

- gdy chcemy poł

ą

czy

ć

 si

ę

 z komputerem lokalnym na porcie 4444; 

Socket s = new Socket("SOLARIS", 4444); 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 18 

- gdy chcemy poł

ą

czy

ć

 si

ę

 z komputerem maj

ą

cym nazw

ę

 

SOLARIS

 w 

sieci lokalnej LAN na porcie 4444; 

Socket s = new Socket("194.29.130.225", 4444); 

-  gdy  chcemy  poł

ą

czy

ć

  si

ę

  z  komputerem  o  danym  adresie  IP  na 

porcie 4444; 

Socket s = new Socket("www.ia.pw.edu.pl", 4444); 

- gdy chcemy poł

ą

czy

ć

 si

ę

 z danym serwerem internetowym na porcie 

4444. 
 

Tworz

ą

c  gniazdko  do  nasłuchu

  typu 

ServerSocket

,  podajemy  tylko 

numer portu, na którym ma by

ć

 prowadzony nasłuch, np.: 

ServerSocket ss = new ServerSocket(4444); 

Numer  portu

  musi  zawiera

ć

  si

ę

  w  przedziale 

[0,...,65535]

.  Jednak

Ŝ

niektóre numery portów s

ą

 zajmowane przez usługi sieciowe takie, jak 

standardowe  protokoły  transmisji,  np.:  21  przez  FTP,  80  przez  HTTP. 
Ponadto pewnych portów u

Ŝ

ywaj

ą

 same systemy operacyjne.  

W  rezultacie  u

Ŝ

ywanie  numerów  portów  z  przedziału 

[0,...,1023]

  jest 

zabronione. 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 19 

2) Realizacja prostej komunikacji  

Przykład 

14.5.a

Nawi

ą

zanie 

komunikacji 

pomi

ę

dzy 

dwoma 

programami.  Najpierw  przedstawiamy  program  serwera  nasłuchuj

ą

cy 

na gniazdku i odbieraj

ą

cy pojawiaj

ą

ce si

ę

 dane. 

//Serwer.java 

import java.io.*; 
import java.net.*; 
public class
 Serwer { 
 

public static void main(String args[]) throws IOException { 

 

 

ServerSocket ss = new ServerSocket(4444); 

// Gniazdko nasłuchu 

 

 

while(true)  { 

// Praca w p

ę

tli  

 

 

  Socket s = ss.accept(); 

// Oczekiwanie na poł

ą

czenie 

 

 

  DataInputStream dis = new DataInputStream(  

 

 

 

 

 

 

 

s.getInputStream()); 

// Pobierz strumień 

 

 

  String msg = dis.readUTF(); 

// Odczyt danych ze strumienia

 

 

 

  System.out.println(msg); 

// Przetwarzaj dane – tu : wyprowadź

 

 

 

  dis.close();  

// Zamkni

ę

cie strumienia 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 20 

 

 

  s.close();  

// Zamkni

ę

cie poł

ą

czenia – gniazdka  

 

 

 

Powy

Ŝ

szy  program  działa,  jak  praktycznie  ka

Ŝ

dy  serwer, 

niesko

ń

czonej p

ę

tli

.  

Nasłuchuje 

on  na

  porcie  4444

,  korzystaj

ą

c  z  gniazdka 

ServerSocket

Sam  proces  oczekiwania  na  poł

ą

czenie  realizowany  jest  w  metodzie 

accept

Gdy  na  wybranym  porcie  pojawi  si

ę

  informacja  o  próbie  uzyskania 

poł

ą

czenia,  nast

ę

puje 

zako

ń

czenie  nasłuchu

  i 

utworzenia

 

gniazdka 

Socket

.  Dalej  nast

ę

puje 

odczytanie  strumienia  podł

ą

czonego

  do 

gniazdka,  zawarto

ść

  jest  wypisywana  na  ekran,  a 

strumie

ń

  i  gniazdo 

s

ą

 zamykane. 

Nast

ę

pnie  program  serwera  znowu  czeka

  na  prób

ę

  nawi

ą

zania 

poł

ą

czenia.  Proste  u

Ŝ

ycie  p

ę

tli  niesko

ń

czonej  powoduje, 

Ŝ

e  aby  go 

zako

ń

czy

ć

, musimy u

Ŝ

y

ć

 kombinacji klawiszy 

Ctrl+C

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 21 

Przykład  14.5.b.

  Wysłanie  danych  przez  klienta.  Program  klienta 

tworzy  gniazdko 

Socket

  w  celu  nawi

ą

zania  poł

ą

czenia  z  lokalnym 

komputerem na porcie 4444. Po zaakceptowaniu poł

ą

czenia tworzony 

jest strumie

ń

, przez który przesyłany jest obiekt typu 

String

. Nast

ę

pnie 

strumie

ń

 i gniazdko s

ą

 zamykane, a program ko

ń

czy działanie. 

//Klient.java 

import java.io.*; 
import java.net.*; 
public class 
Klient { 
 public static void main(String args[]) throws IOException { 
  Socket s = new Socket("localhost", 4444);  
  DataOutputStream dos = new DataOutputStream(s.getOutputStream()); 
  dos.writeUTF("Witaj w sieci!"); 

// Prze

ś

lij napis do serwera 

  dos.close(); 

// Zamknij strumie

ń

 

  s.close(); 

// Zamknij gniazdko 

 } 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 22 

Podsumowanie przykładu 14.5 

Musimy  najpierw  skompilowa

ć

  oba  programy  i  uruchomi

ć

  program 

serwerowy.  Nast

ę

pnie  w  drugim  okienku-konsoli  uruchamiamy 

program  kliencki.  Ka

Ŝ

de  kolejne  uruchomienie  programu-klienta 

spowoduje  wy

ś

wietlenie  jednego  napisu  w  programie-serwerze,  co 

oznacza, 

Ŝ

e poł

ą

czenie jest skutecznie nawi

ą

zywane.  

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 23 

14.3 Przykład aplikacji klient - serwer 

Aplikacje sieciowe s

ą

  tworzone  w celu zdalnego wykonywania jakich

ś

 

konkretnych  czynno

ś

ci  b

ą

d

ź

  udost

ę

pniania  usług,  czy  zasobów. 

Zobaczmy  przykład  aplikacji 

klient-serwer

,  która  -  poza 

nawi

ą

zaniem

 

ł

ą

czno

ś

ci

  -  b

ę

dzie  realizowała 

zdaln

ą

  obsług

ę

 

trzech  polece

ń

 

systemowych: 

 

list

 - wy

ś

wietlenie zawarto

ś

ci bie

Ŝą

cego katalogu na serwerze;  

 

get

 - pobranie pliku z serwera i wy

ś

wietlenie go na konsoli klienta;  

 

exit

 - zako

ń

czenie pracy klienta.  

 

1) Program serwera 
Przykład  14.6.

  Program  serwera  pracuje  w  p

ę

tli  niesko

ń

czonej 

nasłuchuj

ą

c  na 

porcie  4444

  i  oczekuj

ą

c  na 

Ŝą

dania  poł

ą

cze

ń

  od 

klientów.  Gdy  takie  nadchodz

ą

  -  ka

Ŝ

demu  przydzielany  jest  osobny 

w

ą

tek do obsługi.  

//PlikSerwer.java 

import java.io.*; 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 24 

import java.net.*; 

/* W

ą

tek obsługuj

ą

cy  poł

ą

czenie pracuje w  p

ę

tli niesko

ń

czonej. Czyta 

on  ze  strumienia  podł

ą

czonego  do  gniazdka  obiekt  serializowalnej 

klasy  programisty 

Command

,  a  dokładniej  -  najpierw  czyta  obiekt  ze 

strumienia, a nast

ę

pnie zmienia jego typ na 

Command

. */ 

public class PlikSerwer implements Runnable { 
 Socket socket
 public PlikSerwer(Socket s) { 
  socket = s; 
 } 
 public void run() { 

// Metoda dla w

ą

tku 

  try   { 
   ObjectInputStream ois =  
   new ObjectInputStream(socket.getInputStream()); 

// Strumień wejściowy 

   ObjectOutputStream oos =                                          

// i wyjściowy

 

                              new ObjectOutputStream(socket.getOutputStream()); 
   while(true) { 

// P

ę

tla niesko

ń

czona 

    Command cmd = (Command)ois.readObject(); 

// Odczyt obiektu 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 25 

/*  Nast

ę

pnie  sprawdza  zawarto

ść

  odczytanego  obiektu  i  w  zale

Ŝ

no

ś

ci 

od niej tworzy odpowiedni obiekt klasy programisty 

Response

.  

Tworzonemu obiektowi przekazywany jest efekt obsługi polecenia, tzn. 
napis na zako

ń

czenie pracy, lista plików w katalogu bie

Ŝą

cym serwera, 

czy zawarto

ść

 konkretnego pliku. */ 

    // Obsługa komendy zako

ń

czenia pracy klienta 

    if (cmd.getCommand() == Command.EXIT)  { 
        send(oos, new Response(cmd, "Do zobaczenia")); 
     break
    } 

    // Obsługa wy

ś

wietlania zawarto

ś

ci bie

Ŝą

cego katalogu serwera 

    else if (cmd.getCommand() == Command.LIST) { 
        File file = new File("."); 

// Plik  - aktualny katalog 

        send(oos, new Response(cmd, file.list())); 

// Lista plików 

    } 
    else if (cmd.getCommand() == Command.GET) { 

    // Obsługa wy

ś

wietlenia zawarto

ś

ci danego pliku na serwerze

 

     String filename = (String) cmd.getCommandArg(); 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 26 

     File file = new File(filename); 
     byte buff [] = new byte[(int) file.length()]; 
     FileInputStream fis = new FileInputStream(file); 
     fis.read(buff); 

// Wczytaj dane z podanego pliku 

     fis.close(); 
     send(oos, new Response(cmd, buff)); 

// Prze

ś

lij zawarto

ść

 pliku 

    } 
    else {  send(oos, new Response(cmd, "Nieznana komenda")); } 
   } 
   ois.close(); 

// Zamknij strumienie wej

ś

ciowy 

   oos.close(); 

//             -“-               wyj

ś

ciowy 

   socket.close(); 

// Zamknij gniazdko

 

  } 
  catch(Exception e) { 
     System.out.println(e); 
  } 
 } 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 27 

/* Obiekt zapisywany jest do strumienia wyj

ś

ciowego – czyli resultat 

przesyłany jest do klienta w obiekcie klasy 

Response

. */

 

 private void send(ObjectOutputStream oos, Response response) 
         throws IOException { 
      oos.writeObject(response); 
      oos.flush(); 
 } 

/*  Program  serwera  pracuje  w  p

ę

tli  niesko

ń

czonej  nasłuchuj

ą

c  na 

porcie  4444

  i  oczekuj

ą

c  na 

Ŝą

dania  poł

ą

cze

ń

  od  klientów.  Gdy  takie 

nadchodz

ą

 - ka

Ŝ

demu przydzielany jest osobny w

ą

tek do obsługi. */ 

 public static void main(String args[]) throws IOException { 
     ServerSocket ss = new ServerSocket(4444); 

// Utwórz gniazdko 

     System.out.println("Nasłuchiwanie na porcie 4444..."); 
     while(true) { 
       Socket s = ss.accept(); 

// Oczekuje na 

Ŝą

danie od klienta 

       System.out.println("Akceptuję połączenie z: "+s.getInetAddress()); 
       (new Thread(new PlikSerwer(s))).start(); 

// Uruchom w

ą

tek obsługi 

  } 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 28 

 } 

2) Klasy pomocnicze w tym przykładzie 

Przykład  14.7

.  Opiszemy  tu  klas

ę

  pomocnicz

ą

  serwera 

Command.

 

Klasa  pomocnicza  zawiera  deklaracje  stałych  identyfikuj

ą

cych 

polecenia  wydawane  serwerowi  przez  klienta.  Zawiera  te

Ŝ

  zmienn

ą

 

okre

ś

laj

ą

c

ą

  bie

Ŝą

ce  polecenie  wydane  serwerowi  oraz  metody  j

ą

 

obsługuj

ą

ce.  Cała  klasa  jest  serializowana,  aby  jej  obiekty  mogły  by

ć

 

zapisywane do strumieni. 

//Command.java 

import java.io.*; 
public class
 Command implements Serializable { 
 public final static int LIST = 0; 
 public final static int GET  = 1; 
 public final static int EXIT = 2; 
 private int cmd; 
 private Object arg; 
 public Command (int cmd) { 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 29 

  this.cmd = cmd; 
 } 
 public Command (int cmd, Object arg) { 
  this.cmd = cmd; 
  this.arg = arg; 
 } 
 public int getCommand() { 
  return cmd; 
 } 
 public Object getCommandArg() { 
  return arg; 
 } 

Przykład 14.8.

 Opiszemy teraz klas

ę

 

Response

Klasa zawiera prywatne 

pole  do  przechowywania  obiektu  b

ę

d

ą

cego  rezultatem  realizacji 

polecenia  klienta.  Dzi

ę

ki  serializacji  obiekty  tej  klasy  mog

ą

  by

ć

 

przesyłane za pomoc

ą

 strumieni, tak samo jak obiekty 

Command

// Response.java 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 30 

import java.io.*; 
public class
 Response implements Serializable { 
 private Object data; 
 private Command cmd; 
 public Response (Command cmd, Object data) { 
  this.cmd = cmd; 
  this.data = data; 
 } 
 public Object getData() { 
  return data; 
 } 
 public Command getCommand() { 
  return cmd; 
 } 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 31 

3) Program klienta w tym przykładzie 

Przykład 14.9. 

Program klienta. 

// PlikKlient.java 

import java.io.*; 
import java.net.*; 
public class
 PlikKlient { 
   Socket socket
   ObjectOutputStream oos; 
   ObjectInputStream ois; 
 public PlikKlient(String host, int port) throws IOException { 
   socket = new Socket(host, port); 
   oos = new ObjectOutputStream(socket.getOutputStream()); 
   ois = new ObjectInputStream(socket.getInputStream()); 
 } 
 public void go() { 
  try  { 
   BufferedReader br = new BufferedReader(  

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 32 

new InputStreamReader(System.in)); 

   while(true) { 
    System.out.print("Zdalny>"); 
    System.out.flush(); 
    String line = br.readLine(); 

    // Komenda końca pracy klienta 

    if (line.startsWith("exit")) { 
     Command cmd = new Command(Command.EXIT); 
     Response response = send(cmd); 
     System.out.println(response.getData()); 
     System.exit(0); 
    } 

    //Komenda wyświetlenia zawartości bieŜącego katalogu serwera 

    else if (line.startsWith("list")) { 
     Command cmd = new Command(Command.LIST); 
     Response response = send(cmd); 
     String list [] = (String []) response.getData(); 
     for (int i=0; i<list.length; i++) { 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 33 

      System.out.println(list[i]); 
     } 
    } 

    //Komenda wyświetlenia zawartości danego pliku na serwerze 

    else if (line.startsWith("get")) { 
     Command cmd = new Command(Command.GET, line.substring(4)); 
     Response response = send(cmd); 
     byte content [] = (byte []) response.getData(); 
     System.out.println(new String(content)); 
    } 
    else 
     System.out.println("Nieznana komenda: "+line); 
    } 
   } 
  } 
  catch (Exception e) { 
   System.out.println(e); 
  } 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 34 

 } 
 private Response send(Command cmd) throws Exception { 
  oos.writeObject(cmd); 
  oos.flush(); 
  return (Response) ois.readObject(); 
 } 
 public static void main(String args[]) throws IOException { 
  (new PlikKlient("localhost", 4444)).go(); 
 } 

Program  klienta  odczytuje  z  konsoli  polecenia  od  u

Ŝ

ytkownika, 

zapisuje  je  w  postaci  obiektów  do  strumienia  podł

ą

czonego  do 

gniazdka  pracuj

ą

cego  na  porcie  4444,  odczytuje  ze  strumienia 

przychodz

ą

ce z serwera odpowiedzi i wy

ś

wietla je na konsoli. 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 35 

4) Uruchomienie całej aplikacji  

Po  skompilowaniu  wszystkich  klas  uruchamiamy  serwer,  a  nast

ę

pnie 

klienta  (lub  wielu  klientów)  -  oczywi

ś

cie  ka

Ŝ

dy  z  programów 

uruchamiany jest w osobnym okienku konsoli. 

Gdy  oba  programy  znajduj

ą

  si

ę

  na  jednym  komputerze  (tj.  testujemy 

poł

ą

czenie  na  komputerze  lokalnym),  nie  musz

ą

  by

ć

  umieszczone  w 

jednym  katalogu.  Jednak

Ŝ

e  wtedy  zarówno  w  katalogu  z  serwerem  - 

jak  i  z  klientem  -  musz

ą

  znajdowa

ć

  si

ę

  skompilowane  klasy 

pomocnicze. 

Po  uruchomieniu  obu  programów  mo

Ŝ

emy  wyda

ć

  w  konsoli  klienta 

które

ś

 z trzech obsługiwanych polece

ń

 i zobaczy

ć

 efekt jego działania. 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 36 

14.4 RMI (Remote Method Invocation)

 

– model DOA

  

1) Model DOA 

Mechanizm  RMI  wykorzystuje 

model  komunikacji  klient-serwer

  i 

realizuje  paradygmat  programowania  obiektowego  -  rozproszonego 
(

DOA – distributed object application

): 

 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 37 



 

program  serwera

  tworzy  obiekty,  udost

ę

pnia  referencje  do  nich 

(dzi

ę

ki  ich  zarejestrowaniu  poprzez 

rmiregistry

)  i  oczekuje  na 

wywołania metod na tych obiektach inicjowane przez klientów; 



 

programy  klientów

  uzyskuj

ą

  referencj

ę

  do  zdalnego  obiektu  i 

wywołuj

ą

 na nim metody.  

 

2) Typowy schemat współpracy klient - serwer w RMI 

(A) Zdalny interfejs i zdalny obiekt  

Aplikacja z u

Ŝ

yciem 

Java RMI

 składa si

ę

 z interfejsów i klas, przy czym 

spodziewamy  si

ę

Ŝ

e  implementacje  metod  mog

ą

  by

ć

  rozproszone  po 

Ŝ

nych maszynach.  

 

Zdalnym  obiektem

  nazywamy  obiekt,  którego  metody  mog

ą

  by

ć

 

wywoływane  z  ró

Ŝ

nych  maszyn  wirtualnych  Javy.  Takie  metody 

deklarowane s

ą

 w tzw. 

zdalnych interfejsach



 zdalny interfejs dziedziczy po interfejsie 

java.rmi.Remote

 , 



 w  li

ś

cie  wyj

ą

tków  ka

Ŝ

dej  metody  tego  interfejsu  istnieje  deklaracja 

wyj

ą

tku 

java.rmi.RemoteException

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 38 

 

Serwer 

zwi

ą

zuje  nazw

ę

  w  rejestrze  ze  swoim

  zdalnym  obiektem,

 

który mo

Ŝ

e by

ć

 wołany zdalnie.  

 

(B) Pobranie obiektu i wywołanie metody 
Klient 

odnajduje  zdalny  obiekt  w 

rejestrze

  poprzez  jego  nazw

ę

  i 

wywołuje

 

na nim 

metod

ę

 interfejsu.  

 
RMI  nie  kopiuje  zdalnego  obiektu  do  programu  “odbiorcy”  lecz 
przekazuje  “

namiastk

ę

”  (stub)

  zdalnego  obiektu  –  reprezentuje  ona 

referencj

ę

 do zdalnego obiektu. Ta referencja mo

Ŝ

e by

ć

 konwertowana 

na  dowolny 

zdalny  interfejs

  implementowany  przez  klas

ę

  zdalnego 

obiektu.  Klasa  zdalnego  obiektu  pełni  w  programie  klienta  rol

ę

 

po

ś

rednika (proxy) w przekazie danych.  

Zdalnie  mo

Ŝ

na  wywoła

ć

  wył

ą

cznie  metody  zdalnych  interfejsów  danej 

klasy

.  

 

(C) Dynamiczne ładowanie definicji klasy  

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 39 

Je

ś

li  klasa  obiektu 

nie  jest  zdefiniowana

  na  maszynie  “odbiorcy 

obiektu” to RMI pobierze od „nadawcy” i prze

ś

le ten kod.  

Mechanizm  RMI  korzysta  z  istniej

ą

cego 

serwera  sieciowego

  dla 

przekazywania 

bajtkodu klas pomi

ę

dzy klientem a serwerem

 – w obie 

strony.  

 

3) Podstawowe klasy i interfejsy w 

java.rmi

 

 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 40 

4) Tworzenie aplikacji rozproszonej w 

RMI 

 

Nale

Ŝ

y:  



 zdefiniowa

ć

  komponenty  aplikacji  (lokalne  i  zdalne  obiekty,  zdalne 

interfejsy, implementacje metod);  



 skompilowa

ć

  program  (kompilator 

javac

)  i  wygenerowa

ć

  „namiastki” 

zdalnych obiektów (kompilator 

rmic

); 



 udost

ę

pni

ć

 klasy i namiastki obiektów w sieci, 



 uruchomi

ć

  aplikacj

ę

  (dokona

ć

  rejestracji  w  rejestrze  zdalnych 

obiektów RMI, uruchomi

ć

 serwer i klientów).  

 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 41 

14.5 

Przykład 

aplikacji 

rozproszonej 

dynamicznie 

ładowanym kodzie 

Przykład 14.10.

 Projekt 

serwera oblicze

ń

 w oparciu o mechanizm RMI 

Serwer akceptuje zadania pochodz

ą

ce od klienta, wykonuje te zadania 

i  zwraca  wyniki.  Serwer  składa  sie  ze  zdalnego  interfejsu 

Compute

  i 

klasy 

ComputeEngine

  implementuj

ą

cej  ten  interfejs,  przeznaczonej  dla 

utworzenia  zdalnego  obiektu.  W  metodzie 

main

  tej  klasy  nast

ę

puje 

utworzenie tego obiektu, jego zarejestrowanie i ustawienie menad

Ŝ

era 

zabezpiecze

ń

 (security manager). 

 

Interfejsy typu “Remote Interface”  

W  przykładzie  wyst

ę

puj

ą

  2  interfejsy  zdalne  deklaruj

ą

ce  po  jednej 

metodzie. 

 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 42 

Interfejs 

Compute

  serwera 

ComputeEngine

  pozwala  na  dostarczanie 

zadania do wykonania. 

package compute
import java.rmi.Remote
import java.rmi.RemoteException
public interface Compute extends Remote { 

    Object executeTask(Task t) throws RemoteException

 
Dziedziczenie z interfejsu 

java.rmi.Remote

 oznacza, 

Ŝ

e: 



 

metody 

dziedziczonego  interfejsu  mog

ą

  by

ć

 

wołane  z  dowolnej 

maszyny wirtualnej Javy; 



 obiekt z implementacj

ą

 tego interfejsu staje si

ę

 

zdalnym obiektem

 
Zdalna 

metoda 

musi 

deklarowa

ć

 

zgłaszanie 

wyj

ą

tku  

java.rmi.RemoteException

. Jest to 

sprawdzalny wyj

ą

tek

.   

 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 43 

Interfejs  klienta 

Task

  wyznacza  sposób  wykonania  przez  serwer 

powierzonego zadania. 

package compute
 
import java.io.Serializable
 
public interface Task extends Serializable { 
    Object execute(); 

 
Dziedziczenie interfejsu po 

java.io.Serializable

 oznacza, 

Ŝ

e obiekt klasy 

implementuj

ą

cej  ten  interfejs  mo

Ŝ

e  zosta

ć

  zamieniony  na  strumie

ń

 

bajtów i jednoznacznie zrekonstruowany z niego.  
 
Obiekty  ró

Ŝ

nych  klas  mog

ą

  zosta

ć

  przekazane  do  obiektu 

Compute

je

ś

li tylko klasy implementuj

ą

 interfejs 

Task.

 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 44 

Implementacja zdalnego interfejsu  
Klasa  u

Ŝ

ytkownika 

engine.ComputeEngine

  implementuje  interfejs  zdalny 

Compute

, a w jej metodzie 

main

 inicjalizowany jest obiekt tej klasy. 

package engine; 
import java.rmi.*; 
import java.rmi.server.*; 
import 
compute.*; 
 
public class ComputeEngine extends UnicastRemoteObject 
                           implements Compute  

// Konstruktor –  

    public ComputeEngine() throws RemoteException { 
        super(); 

// Wywoła metod

ę

 

exportObject

  

// Konstruktor nadklasy został by wywołany – nawet przy braku 

super();

  

    } 

// Metoda dla pobrania obiektu klienta do wykonania 

    public Object executeTask(Task t) { 
        return  t.execute(); //  

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 45 

    } 

// Metoda 

main

 dla ustawienia obiektu 

    public static void main(String[] args) { 
      if (System.getSecurityManager() == null) { 
          System.setSecurityManager(new RMISecurityManager()); 
        } 
        String name = "//host/Compute";  

// Okre

ś

la nazw

ę

 obiektu  

   

 

 

 

 

 

 

 

// zdalnego serwera 

      try {  
         Compute engine = new ComputeEngine(); 

// Utworzy obiekt

 

       Naming.rebind(name, engine); 

// Rejestracja obiektu zdalnego

 

        System.out.println("Compute: związany"); 
        } catch (Exception e) { 

 

 

 

 System.err.println("Compute: wyjątek " + e.getMessage()); 

            e.printStackTrace(); 
        } 
    } 

// Koniec metody

 

main

 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 46 

Komentarz 
 
1) Klasa serwera implementuje zdalny interfejs:  

public class ComputeEngine extends UnicastRemoteObject 
                           implements Compute
 

i dziedziczy po klasie bazowej 

java.rmi.server.UnicastRemoteObject

.  

 
Ta klasa zapewnia podstawow

ą

 funkcjonalno

ść

 zdalnego obiektu:  



 implementuje  metody  klasy 

java.lang.Object

  (

equals,  hashCode

toString

) odpowiednio dla zdalnego obiektu;  



 zawiera  konstruktory  i  metody  statyczne  przewidziane  do 

wyeksportowania zdalnego obiektu (

exportObject()

) tzn. aby zdalny 

obiekt mógł odbiera

ć

 wezwania od klientów; 



 zapewnia poł

ą

czenia „point-to-point” w oparciu o 

gniazdka

 
Inna  klasa  dla  zdalnych  obiektów 

java.rmi.activation.Activatable

  (dla 

zdalnych obiektów wywoływalnych na 

Ŝą

danie). 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 47 

2) Konstruktor klasy

 

ComputeEngine

 :  

public ComputeEngine() throws RemoteException 
    super(); 

// Nast

ę

puje tu te

Ŝ

 eksportowanie obiektu 

Nale

Ŝ

y  zadeklarowa

ć

  zgłaszalno

ść

  w

ą

tku 

RemoteException

,  który 

zostanie zgłoszony, gdy nie powiedzie si

ę

 eksport obiektu. 

 

3) Implementacje metod interfejsów zdalnych 

Klasa implementuje jedn

ą

 metod

ę

 interfejsu zdalnego 

Compute

 metod

ę

 

executeTask

public Object executeTask(Task t) { 
    return t.execute(); 

Metoda  wyznacza  sposób  komunikacji  pomi

ę

dzy  obiektem  klasy 

ComputeEngine

  a  jego  klientami.  Klienci  przekazuj

ą

  serwerowi  obiekt 

klasy  implementuj

ą

cej  interfejs 

Task

  i  posiadaj

ą

cej  implementacj

ę

 

metody 

execute

.  

 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 48 

4) Klasa klienta 

Do serwera mo

Ŝ

e by

ć

 przekazany obiekt dowolnego typu o ile jest to: 

zmienna  typu  prostego,  obiekt  zdalny  lub  serializowalny  obiekt  (tzn. 
jego klasa implementuje interfejs 

java.io.Serializable

).  

 

Sposoby przekazywania obiektów zdalnych, parametrów i wyników. 
Zdalne  obiekty

  przekazywane  s

ą

  przez 

referencj

ę

.  Po  stronie  klienta 

tworzona  jest 

namiastka  obiektu

  (stub)  pełni

ą

ca  rol

ę

 

proxy 

(przekazuj

ą

c

ą

 odwołania do rzeczywistego obiektu serwera). 

Zwykłe  obiekty

  przekazywane  s

ą

  przez 

warto

ść

  poprzez  ich 

serializacj

ę

.  W  zdalnym  wywołaniu  metody  –  zwykłe  obiekty 

(parametry, zwracany wynik, wyj

ą

tki) s

ą

 przekazywane przez

 warto

ść

 – 

tworzona jest ich kopia w zdalnej wirtualnej maszynie Javy. 
 

5) Metoda 

main

 

Metoda 

main()

 klasy 

ComputeEngine

 nie jest elementem interfejsu, czyli 

nie jest metod

ą

 wywoływaln

ą

 zdalnie.  

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 49 

W pierwszej kolejno

ś

ci metoda 

main()

 powinna utworzy

ć

 i zainstalowa

ć

 

zarz

ą

dc

ę

  polityki  bezpiecze

ń

stwa  (

Security  Manager

),  w  przeciwnym 

razie mechanizm 

RMI

 nie zezwoli na ładowanie zdalnych klas.  

 

RMI 

dost

ę

pna  jest  klasa 

RMISecurityManager

,  która  realizuje 

pdobn

ą

 polityk

ę

 bezpiecze

ń

stwa, jak ta stosowana dla apletów. 

if (System.getSecurityManager() == null) { 
    System.setSecurityManager(new RMISecurityManager()); 

 
Nast

ę

pnie 

utworzony 

zostaje 

obiekt 

klasy 

ComputeEngine

 

udost

ę

pniony klientom na anonimowym porcie: 

Compute engine = new ComputeEngine(); 

// Utworzenie obiektu 

 
Mechanizm  RMI  zarz

ą

dza 

rejestrem  zdalnych  obiektów

,  z  którego 

mo

Ŝ

na  pobra

ć

  referencj

ę

  do  zdalnego  obiektu  na  podstawie  jego 

nazwy.  Rejestr  ten  mo

Ŝ

e  by

ć

  wspólny  dla  wszystkich  serwerów  na 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 50 

danej  maszynie  albo  te

Ŝ

  proces  serwera  mo

Ŝ

e  posiada

ć

  odr

ę

bny  taki 

rejestr. 
 
Metody  dost

ę

pu  do  rejestru  zdalnych  obiektów  wyznacza  interfejs 

java.rmi.Naming

 (zwi

ą

zanie, rejestracja, pobranie). 

 
W metodzie 

main

 klasy 

ComputeEngine 

nadajemy nazw

ę

  

 

String name = "//host/Compute"; 

// 

host

 to nazwa maszyny 

 

// 

Compute

 to nazwa identyfikuj

ą

ca obiekt zdalny w rejestrze. 

i rejestrujemy obiekt 

engine

 klasy 

ComputeEngine

 w rejestrze:  

Naming.rebind(nameengine);  

 
Podczas wywołania 

rebind()

 mo

Ŝ

e powsta

ć

 wyj

ą

tek: 

RemoteException

 . 

Parametry wywołania metody 

Naming.rebind

 : 

pierwszy  parametr  jest  typu 

java.lang.String

  o  postaci  adresu 

URL 

;

 

Np. 

//host:1234/objectname

  

Je

ś

li brak jest numeru portu to przyjmuje si

ę

 domy

ś

lny port: 1099.  

 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 51 

 
Po zarejestrowaniu obiektu zdalnego i wydruku informacji o gotowo

ś

ci 

do pracy metoda 

main()

 

ko

ń

czy si

ę

 wykonywa

ć

 (

w

ą

tek główny ko

ń

czy

 

swoje wykonanie !).  
Jednak 

istnieje  referencja

  do  obiektu  przekazana  innemu  obiektowi  – 

bed

ą

cemu rejestrem zdalnych obiektów.  

Mechanizm  RMI  pilnuje,  aby  proces  dla  obiektu 

ComputeEngine

  nadal 

istniał.  

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 52 

Przykład  14.11

  Program  klienta  musi  definiowa

ć

  zadanie,  które  ma 

wykona

ć

 w jego imieniu serwer. W tym przykładzie klient składa si

ę

 z 2 

klas: 

ComputePi

  –  w  celu  pobrania  obiektu  serwera 

Compute

  ;   

Pi

  - 

klasa implementuj

ą

ca interfejs 

Task

 i definiuj

ą

ca zadanie.  

Definicja 

Task

package compute; 
public interface Task extends java.io.Serializable { 
    Object execute(); 

 
Klasa klienta 

client.ComputePi

 

package client; 
 
import java.rmi.*; 
import java.math.*; 
import
 compute.*; 
 
public class ComputePi { 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 53 

    public static void main(String args[]) { 
        if (System.getSecurityManager() == null) { 
            System.setSecurityManager(new RMISecurityManager()); 
        } 
        try { 
         

// Nazwa serwera w formacie URL 

            String name = "//" + args[0] + "/Compute";  
        

// Pobierz referencje do obiektu zdalnego serwera: 

            Compute comp = (ComputeNaming.lookup(name); 
            Pi task = new Pi(Integer.parseInt(args[1])); 

       // Wywołaj metod

ę

 

executeTask

 obiektu zdalnego 

comp

  

       // przekazuj

ą

c zadanie do wykonania 

task 

            BigDecimal pi = (BigDecimal) (comp.executeTask(task)); 
            System.out.println(pi); 

// Wydrukuj obliczony wynik 

        } catch (Exception e) { 
            System.err.println("ComputePi exception: " +  
                               e.getMessage()); 
            e.printStackTrace(); 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 54 

        } 
    } 

 
Przepływ  informacji  pomi

ę

dzy  obiektem  klienta  klasy 

ComputePi

rejestrem  obiektów  zdalnych 

rmiregistry

  i  obiektem  serwera 

ComputeEngine

.  

 

 

Komentarz 
1) Klasa klienta instaluje menad

Ŝ

era bezpiecze

ń

stwa 

Jest to niezb

ę

dne, gdy

Ŝ

 RMI mo

Ŝ

e ładowa

ć

 kod do procesu klienta – w 

tym przypadku namiastk

ę

 obiektu 

ComputeEngine

 . 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 55 

Podobnie  jak  serwer  równie

Ŝ

  klient  korzysta  tu  z  menad

Ŝ

era 

bezpiecze

ń

stwa dost

ę

pnego w systemie RMI. 

 

2) Klient tworzy nazw

ę

 do nadzoru obiektu zdalnego typu 

Compute

 

Pierwszy parametr z linii wywołania programu, 

args[0]

, stanowi nazw

ę

 

zdalnej maszyny, na której wykonuje si

ę

 object typu 

Compute

Klient  odwołuje  si

ę

  do  metody 

Naming.lookup

  w  celu  odszukania 

obiektu po jego nazwie w rejestrze zdalnych obiektów zdalnego hosta. 
Parametr  metody  (typu 

String

)  ma  t

ę

  sam

ą

  składni

ę

  adresu  URL  jak 

parametr metody 

Naming.rebind

 w przypadku rejestracji obiektu przez 

serwer. 
 

3) Klient tworzy nowy obiekt klasy 

Pi

 

Argumentem  wywołania  konstruktora 

Pi

  jest  drugi  paramer  z  inii 

wywołania  programu  klienta, 

args[1]

,  podaj

ą

cy  liczb

ę

  pozycji 

dziesi

ę

tnych dla oblicze

ń

 

4) Klient wywołuje metod

ę

 

executeTask

 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 56 

Klient wywołuje metod

ę

 

executeTask

 zdalnego obiektu typu 

Compute

.  

 
Defnicja klasy 

client.Pi

, która implementuje inerfejs 

Task

:  

package client; 
import compute.*; 
import java.math.*; 
public class Pi
 implements Task { 

    /** Stałe potrzebne dla obliczenia watości pi */ 

    private static final BigDecimal ZERO = BigDecimal.valueOf(0); 
    private static final BigDecimal  ONE =  BigDecimal.valueOf(1); 
    private static final BigDecimal FOUR = BigDecimal.valueOf(4); 

    /** Tryb zaokrąglania */ 

private static final int roundingMode = BigDecimal.ROUND_HALF_EVEN; 

    /** Precyzja - liczba pozycji po kropce dziesiętnej */ 

    private int digits; 

     

    /** Konstruktor */ 

    public Pi(int digits) { 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 57 

        this.digits = digits; 
    } 

 

    /** Oblicz pi */ 

    public Object execute() { 
        return computePi(digits); 
    } 

    /** według formuły  
     *          pi/4 = 4*arctan(1/5) - arctan(1/239) 
     * i po rozwinięciu arctan(x) w szereg.   
     */ 

    public static BigDecimal computePi(int digits) { 
        int scale = digits + 5; 
        BigDecimal arctan1_5 = arctan(5, scale); 
        BigDecimal arctan1_239 = arctan(239, scale); 
        BigDecimal pi = arctan1_5.multiply(FOUR).subtract( 
                                  arctan1_239).multiply(FOUR); 
        return pi.setScale(digits,  

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 58 

                           BigDecimal.ROUND_HALF_UP); 
    } 

    /** 
     * Oblicza wartość arct tangens w radianach według wzoru: 
     * arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + (x^9)/9 ... 
     */    

    public static BigDecimal arctan(int inverseX, int scale)  
    { 
        BigDecimal result, numer, term; 
        BigDecimal invX = BigDecimal.valueOf(inverseX); 
        BigDecimal invX2 =  
            BigDecimal.valueOf(inverseX * inverseX); 

        numer = ONE.divide(invX, scale, roundingMode); 
        result = numer; 
        int i = 1; 
        do { 
            numer =  
                numer.divide(invX2, scale, roundingMode); 

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 59 

            int denom = 2 * i + 1; 
            term =  
                numer.divide(BigDecimal.valueOf(denom), 
                             scale, roundingMode); 
            if ((i % 2) != 0) {  result = result.subtract(term); 
            } else {  result = result.add(term); 
                      } 
            i++; 
        } while (term.compareTo(ZERO) != 0); 
        return result; 
    } 

Uwaga 

Obiekt klasy 

Compute 

nie wymaga definicji klasy 

Pi

 dopóki obiekt klasy 

Pi

 nie zostanie przekazany jako argument metody 

executeTask

. W tym 

momencie  kod  klasy  ładowany  jest  przez  RMI  do  maszyny  wirtualnej 
wła

ś

ciwej  dla  obiektu  typu 

Compute

,  wołana  jest  metoda 

execute

  klasy 

Pi

 i wykonuje si

ę

 kod tej metody. Wynikowy obiekt typu 

Object

, w tym 

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 60 

przypadku  jest  on  typu 

java.math.BigDecimal

,  jest  przekazywany 

zwrotnie do klienta, w którym nast

ę

puje wydruk tego obiektu. 

 
Z  punktu  widzenia  serwera  –  obiektu  typu 

ComputeEngine

  –  jest  bez 

znaczenia, co oblicza zlecona metoda. Jedyne co musi wiedzie

ć

 obiekt 

klasy  implementuj

ą

cej 

Compute

  o  przekazywanym  obiekcie  to  to, 

Ŝ

jego klasa posiada metod

ę

 

execute

.  

 

Dynamiczne ładowanie klas  

 
Poni

Ŝ

szy rysunek ilustruje sk

ą

d ładowane s

ą

 definicje klas potrzebnych 

programom: 

rmiregistry

,  serwerowi 

ComputeEngine

,  i  klientowi 

ComputePi

 podczas wykonania rozproszonej aplikacji Javy. 

Gdy  serwer 

ComputeEngine

  rejestruje  (bind)  swój  zdalny  obiekt  w 

rejestrze,  rejestr  pobiera  klas

ę

 

ComputeEngine_Stub

  i  interfejsy 

Compute

  i 

Task

,  od  której  ta  klasa  zale

Ŝ

y.  Te  klasy  pobierane  s

ą

  z 

serwera  sieciowego  wła

ś

ciwego  dla 

ComputeEngine 

lub  z  systemu 

plików.  

background image

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 61 

Klient  typu 

ComputePi

  ładuje  klas

ę

 

ComputeEngine_Stub

  z  serwera 

sieci  wła

ś

ciwego  dla 

ComputeEngine

  w  wyniku  wykonania  metody 

Naming.lookup

.  Klient 

ComputePi

  dysponuje  opisami  interfejsów 

Compute

  i 

Task

,  dlatego  te

Ŝ

  ładowane  s

ą

  one  z  jego  lokalnego 

katalogu klas.  

 

Na koniec, do maszyny wirtualnej wykonuj

ą

cej serwer 

ComputeEngine 

ładowany  jest  kod  klasy 

Pi

,  gdy  obiekt  klasy 

Pi

  przekazywany  jest  w 

zdalnym  wywołaniu  metody 

executeTask

  na  rzecz  obiektu  typu 

ComputeEngine

  –  klasa 

Pi

  ładowana  jest  z  serwera  sieciowego 

wła

ś

ciwego dla klienta.  

14. Komunikacja rozproszona 

W. Kasprzak: Programowanie zdarzeniowe 

14 - 62 

Kompilacja i uruchomienie aplikacji

 (serwera i klientów) oraz obliczenie 

warto

ś

ci 

π

.  

1)  Tworzymy  pliki  typu  JAR  (Java  ARchive)  zawieraj

ą

cy  interfejsy  

Compute

  i 

Task

  dla  implementacji  ich  przez  klasy  serwera  i  z 

których korzysta program klienta. 

2)  Tworzymy implementacj

ę

 interfejsu 

Compute

 i instalujemy t

ę

 usług

ę

 

na maszynie udost

ę

pniaj

ą

c j

ą

 klientom.  

3)  Twórcy  programów  klientów,  korzystaj

ą

c  z  interfejsów 

Compute

  i 

Task

, zawartych w pliku JAR, mog

ą

 niezale

Ŝ

nie od siebie utworzy

ć

 

zadanie (

Task

) i program klienta korzystaj

ą

cego z usługi 

Compute

.  

Klasa  klienta 

Pi

  ładowana  jest  na  serwer  podczas  wykonania. 

Podobnie  zdalna  namiastka  dla 

ComputeEngine

  ładowana  jest  z 

serwera na maszynie klienta podczas wykonania. 
Mamy trzy pakiety: 

 compute ( interfejsy 

Compute 

Task

)  

 engine (klasa implementuj

ą

ca 

ComputeEngine

 i jej namiastka)  

 client (kod klienta 

ComputePi

 i implementacja zadania 

Pi

).