Programowanie aplikacji w architekturze Klient-Serwer - UDP



Podobne dokumenty
Przykłady interfejsu TCP i UDP w Javie

Programy typu klient serwer. Programowanie w środowisku rozproszonym. Wykład 5.

Programowanie współbieżne i rozproszone

Programowanie sieciowe

Programowanie rozproszone w języku Java

Java programowanie w sieci. java.net RMI

Networking. Zaawansowane technologie Javy 2019

Język JAVA podstawy. wykład 2, część 1. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Programowanie w języku Java

Aplikacja wielowątkowa prosty komunikator

Klient-Serwer Komunikacja przy pomocy gniazd

Zadanie 2: transakcyjny protokół SKJ (2015)

Komunikacja z użyciem gniazd aplikacje klient-serwer

MODEL WARSTWOWY PROTOKOŁY TCP/IP

Sposoby tworzenia projektu zawierającego aplet w środowisku NetBeans. Metody zabezpieczenia komputera użytkownika przed działaniem apletu.

Serwer współbieżny połączeniowy

Gniazda BSD implementacja w C#

Aplikacja wielow tkowa prosty komunikator

Podstawy i języki programowania

Aplikacje RMI. Budowa aplikacji rozproszonych. Część 2.

Aplikacje RMI

Ćwiczenie 1. Kolejki IBM Message Queue (MQ)

Podstawowe typy serwerów

Wybrane działy Informatyki Stosowanej

JAVA I SIECI. MATERIAŁY:

Sieci komputerowe. Wykład 7: Transport: protokół TCP. Marcin Bieńkowski. Instytut Informatyki Uniwersytet Wrocławski

Aplikacja Sieciowa wątki po stronie klienta

Narzędzia i aplikacje Java EE. Usługi sieciowe Paweł Czarnul pczarnul@eti.pg.gda.pl

Przesyłania danych przez protokół TCP/IP

Ćwiczenie 2. Obsługa gniazd w C#. Budowa aplikacji typu klient-serwer z wykorzystaniem UDP.

Zaawansowane aplikacje WWW - laboratorium

1. Co można powiedzieć o poniższym kodzie? public interface I { void m1() {}; static public void m2() {}; void abstract m3();

Programowanie w języku Java - Wyjątki, obsługa wyjątków, generowanie wyjątków

Kurs programowania. Wykład 10. Wojciech Macyna. 05 maja 2016

Protokoły sieciowe - TCP/IP

Programowanie obiektowe. Literatura: Autor: dr inŝ. Zofia Kruczkiewicz

URL. Budowa URL (ang. Uniform Resource Locator): working#downloading

Adresy URL. Zaawansowane technologie Javy 2019

Zarządzanie ruchem w sieci IP. Komunikat ICMP. Internet Control Message Protocol DSRG DSRG. DSRG Warstwa sieciowa DSRG. Protokół sterujący

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Platformy Programistyczne Zagadnienia sieciowe i wątki

Laboratorium - Przechwytywanie i badanie datagramów DNS w programie Wireshark

Wątek - definicja. Wykorzystanie kilku rdzeni procesora jednocześnie Zrównoleglenie obliczeń Jednoczesna obsługa ekranu i procesu obliczeniowego

Gniazda komunikacji sieciowej w środowisku Java

Java programowanie sieciowe

JAVA. Java jest wszechstronnym językiem programowania, zorientowanym. apletów oraz samodzielnych aplikacji.

Bezpieczne uruchamianie apletów wg

Sieci komputerowe. Zajęcia 3 c.d. Warstwa transportu, protokoły UDP, ICMP

Programowanie obiektowe

sieć 4) Mechanizm RMI jest zazwyczaj wykorzystywany w rozwiązaniach typu klient-serwer.

Multimedia JAVA. Historia

Autor: dr inż. Zofia Kruczkiewicz, Programowanie aplikacji internetowych 1

Obiekty sieciowe - gniazda Komputery w sieci Internet komunikują się ze sobą poprzez:

Sieci komputerowe. Wykład 5: Warstwa transportowa: TCP i UDP. Marcin Bieńkowski. Instytut Informatyki Uniwersytet Wrocławski

Architektura typu klient serwer: przesyłanie pliku tekstowo oraz logowania do serwera za pomocą szyfrowanego hasła

Katedra Architektury Systemów Komputerowych Wydział Elektroniki, Telekomunikacji i Informatyki Politechniki Gdańskiej

Aplikacje w Javie- wykład 11 Wątki-podstawy

Obiektowe programowanie rozproszone Java RMI. Krzysztof Banaś Systemy rozproszone 1

Gniazda surowe. Bartłomiej Świercz. Łódź,9maja2006. Katedra Mikroelektroniki i Technik Informatycznych. Bartłomiej Świercz Gniazda surowe

RPC. Zdalne wywoływanie procedur (ang. Remote Procedure Calls )

Laboratorium Sieci Komputerowych - 2

Wykład 3 / Wykład 4. Na podstawie CCNA Exploration Moduł 3 streszczenie Dr inż. Robert Banasiak

STRUMIENIE DANYCH, SERIALIZACJA OBIEKTÓW

Java jako język programowania

Programowanie obiektowe zastosowanie języka Java SE

Wykład 8: Obsługa Wyjątków

KOMUNIKACJA MIĘDZYPROCESOWA O B S Ł U G A WEJŚCIA/WYJŚCIA

K O M U N I K A C J A MIĘDZYPROCESOWA O B S Ł U G A WEJŚCIA/WYJŚCIA

Ćwiczenie 1. Przygotowanie środowiska JAVA

Metody Metody, parametry, zwracanie wartości

Łukasz Przywarty Wrocław, r. Grupa: WT/N 11:15-14:00. Sprawozdanie z zajęć laboratoryjnych: OpenSSL - API

Programowanie obiektowe

Programowanie obiektowe

Java a dost p do Internetu.

Aplikacje RMI Lab4

Język JAVA podstawy. Wykład 6, część 2. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Wykład 2 Wybrane konstrukcje obiektowych języków programowania (1)

Wykład 4: Protokoły TCP/UDP i usługi sieciowe. A. Kisiel,Protokoły TCP/UDP i usługi sieciowe

Politechnika Łódzka. Instytut Systemów Inżynierii Elektrycznej

5. Model komunikujących się procesów, komunikaty

Wprowadzanie danych z klawiatury. Wyjątki związane z wprowadzaniem danych, przekroczeniem rozmiaru tablicy, dzieleniem przez zero itd.

Plan wykładu. Domain Name System. Hierarchiczna budowa nazw. Definicja DNS. Obszary i ich obsługa Zapytania Właściwości.

Systemy Rozproszone - Ćwiczenie 6

Sieci komputerowe Warstwa transportowa

Programowanie Obiektowe Ćwiczenie 4

Dokumentacja wstępna TIN. Rozproszone repozytorium oparte o WebDAV

Transport. część 2: protokół TCP. Sieci komputerowe. Wykład 6. Marcin Bieńkowski

Java. język programowania obiektowego. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

Pierwsze kroki. Algorytmy, niektóre zasady programowania, kompilacja, pierwszy program i jego struktura

Dzisiejszy wykład. Wzorce projektowe. Visitor Client-Server Factory Singleton

Remote Quotation Protocol - opis

Współbieżność i równoległość w środowiskach obiektowych. Krzysztof Banaś Obliczenia równoległe 1

Rozdział 4 KLASY, OBIEKTY, METODY

Mechanizmy pracy równoległej. Jarosław Kuchta

Programowanie w Sieci Internet Blok 2 - PHP. Kraków, 09 listopada 2012 mgr Piotr Rytko Wydział Matematyki i Informatyki

Architektury systemów rozproszonych LABORATORIUM. Ćwiczenie 1

Aplikacje Internetowe. Najprostsza aplikacja. Komponenty Javy. Podstawy języka Java

Język JAVA podstawy. wykład 2, część 2. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Wątki. Definiowanie wątków jako klas potomnych Thread. Nadpisanie metody run().

Podstawowe protokoły transportowe stosowane w sieciach IP cz.1

Transkrypt:

Katedra Inżynierii Komputerowej Koszalin 2002 Programowanie dla sieci Wykłady i ćwiczenia Dariusz Rataj Część 2 Programowanie aplikacji w architekturze Klient-Serwer - UDP Kontakt email: rataj@man.koszalin.pl Materiały dostępne pod adresem: http://java.ie.tu.koszalin.pl

Spis treści Spis treści... 2 1. Komunikacja datagramowa - UDP... 2 2. Klasy języka Java obsługujące połączenia datagramowe... 3 2.1. Klasa DatagramPacket... 3 2.1.1. Datagramy wysyłane - konstruktory... 3 2.1.2. Datagramy odbierane - konstruktory... 4 2.1.3. Metody pomocnicze klasy DatagramPacket... 4 2.2. Klasa DatagramSocket... 5 2.2.1. Gniazda wysyłające dane... 5 2.2.2. Gniazda odbierające dane... 5 3. Przykłady realizacji usług opartych na UDP...6 3.1. Komunikacja jednokierunkowa... 6 3.2. Proste usługi (przykład realizacji usługi daytime)... 8 3.3. Przesyłanie większych bloków danych (przykład usługi copyfile)... 10 3. Aplety i komunikacja sieciowa... 14 5. Podsumowanie UDP-TCP... 15 Dodatek. Ćwiczenia do samodzielnego wykonania... 15 1. Komunikacja datagramowa - UDP W odróżnieniu od rozwiązań opartych na połączeniach TCP komunikacja datagramowa charakteryzuje się dużą zawodnością. Powodem tego jest, przede wszystkim, brak połączenia między klientem a serwerem a także brak potwierdzenia otrzymania danych realizowanego na poziomie protokołu UDP (w warstwie 4 modelu OSI). Ważną cechą komunikacji opartej na UDP jest brak gniazda serwera. UDP (datagram) Aplikacja wysyłane dane nagłowek UDP wysyłane dane IP nagłowek IP nagłowek UDP wysyłane dane Ethernet nagłowek Ethernet nagłowek IP nagłowek UDP wysyłane dane końcówka Ethernet Rys. 1. Opakowywanie datagramu w komunikacji przez UDP Gniazda datagramowe mogą jedynie wysyłać i odbierać datagramy. Datagram jest pakietem UDP i wiąże się bezpośrednio z fizycznym opakowaniem danych w warstwie transportowej (Rys. 1). - 2 -

Nagłówek pakietu UDP (datagramu) zawiera informacje o porcie docelowym, porcie źródłowym i długości datagramu wraz z nagłówkiem. Wielkość bloku danych datagramu jest ograniczona do wielkości 65507 bajtów co jest spowodowane maksymalną wielkością pakietu IP. W niektórych systemach wielkości te mogą się różnić i w praktyce stosuje się dużo mniejsze bloki danych umieszczane w datagramach. Zalecane jest stosowanie datagramów z blokiem danych nie większym niż 8kB (8192 bajty). Takie ograniczenie ma dostosować wielkość datagramu do różnych rozwiązań w warstwie IP i w warstwie fizycznej a tym samym zmniejszyć zawodność komunikacji. 2. Klasy języka Java obsługujące połączenia datagramowe Do realizacji połączeń datagramowych w języku Java wykorzystywane są dwie klasy: DatagramPacket i DatagramSocket. Klasa DatagramPacket definiuje wysyłany lub odbierany datagram, klasa DatagramSocket wysyła lub odbiera datagramy. Obydwie klasy są wykorzystywane zarówno po stronie serwera jak i klienta. 2.1. Klasa DatagramPacket Obiekt klasy DatagramPacket reprezentuje pakiet UDP inaczej nazywany datagramem. Klasa posiada sześć konstruktorów. Poniżej przedstawione zostały cztery konstruktory stosowane najczęściej w praktyce z czego dwa pierwsze używane są do tworzenia datagramów do wysłania, dwa kolejne do tworzenia obiektów dla datagramów odbieranych. 2.1.1. Datagramy wysyłane - konstruktory Wysyłany datagram zawiera dane: bufor zawierający dane wysyłane, długość bloku danych wysyłanych, adres docelowy oraz port docelowy. public DatagramPacket(byte[] buf, int length, InetAddress address, int port) konstruktor paczki datagramowej z parametrami: buf - tablica wartości typu byte (bufor wysyłanych danych), length - ilość (długość) bajtów wysyłanych - typu int, address - adres docelowy - obiekt typu InetAddress, port - port docelowy - wartość typu int. Drugi z przedstawionych kostruktorów daje możliwość wysłania wybranego fragmentu bufora i posiada dodatkowy parametr offset (przesunięcie) określający początek bloku danych odczytywanych z bufora. public DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) konstruktor paczki datagramowej z parametrami: buf - tablica wartości typu byte (bufor wysyłanych danych), offset - początek (przesunięcie od początku bufora) bloku danych wysyłanych - typu int, length - ilość (długość) bajtów wysyłanych - typu int, address - adres docelowy - obiekt typu InetAddress, port - port docelowy - wartość typu int. Przykładowy fragment poprawnie utworzonego datagramu może wyglądać następująco: byte buf[] = new byte[100]; // bufor wielkości 100 bajtów... // umieszczenie danych w buforze DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getByName("kos.man.koszalin.pl"), 5501 ); - 3 -

2.1.2. Datagramy odbierane - konstruktory Kolejne dwa konstruktory klasy DatagramPacket stosowane są dla datagramów przychodzących. W tym zastosowaniu nie ma potrzeby definiowania adresu i portu (z oczywistych powodów). Definiowany jest jedynie bufor i długość bloku danych danych: public DatagramPacket(byte[] buf, int length) konstruktor datagramu z parametrami: buf - tablica wartości typu byte (bufor przychodzących danych), length - maksymalna ilość (długość) bajtów odbieranych - typu int. Kolejny konstruktor daje możliwość zapisu danych przychodzących począwszy od wybranego miejsca w buforze. public DatagramPacket(byte[] buf, int offset, int length) konstruktor datagramu z parametrami: buf - tablica wartości typu byte (bufor przychodzących danych), offset - początek (przesunięcie od początku bufora) bloku danych przychodzących - typu int, length - maksymalna ilość (długość) bajtów odbieranych - typu int. Przykładowy fragment poprawnie utworzonego datagramu dla danych przychodzących może wyglądać następująco: byte buf[] = new byte[100]; // bufor wielkości 100 bajtów // obiekt gotowy do przyjęcia 100 bajtów danych DatagramPacket packet = new DatagramPacket(buf, buf.length); 2.1.3. Metody pomocnicze klasy DatagramPacket Oprócz konstruktorów tworzących obiekt paczki datagramowej klasa DatagramPacket posiada szereg metod pozwalających na operowanie informacjami zapisanym w datagramie. Można tu wyróżnić dwie grupy: metody ustawiające informacje - setx oraz metody pobierające informacje z datagramu- getx. Metody ustawiające informacje: void setaddress(inetaddress addr) - ustawienie adresu docelowego, void setdata(byte[] buf) - ustawienie bufora, void setdata(byte[] buf, int offset, int length) - ustawienie bufora, długości bufora i przesunięcia w buforze, void setlength(int length) - ustawienie długości bloku danych, void setport(int port) - ustawienie portu docelowego. Metody pobierające informacje: InetAddress getaddress() - pobranie adresu docelowego, byte[] getdata() - pobranie danych z datagramu, int getlength() - pobranie długości bloku danych, int getoffset() - pobranie przesunięcia danych w buforze, int getport() - pobranie portu docelowego. - 4 -

2.2. Klasa DatagramSocket Klasa DatagramSocket służy do tworzenia obiektów gniazd datagramowych, które mogą zarówno wysyłać jak i odbierać dane (datagramy - wcześniej utworzone obiekty DatagramPacket). Podobnie jak w przypadku klasy DatagramPacket klasa DatagramSocket posiada oddzielne konstruktory dla strony odbierającej jak i wysyłającej datagramy. 2.2.1. Gniazda wysyłające dane Dla gniazd wysyłających dane stosowany jest jeden konstruktor klasy DatagramSocket - konstruktor bezparametrowy. Gniazda wysyłające dane mogą przejść w tryb odbierania danych. Nie ma wtedy konieczności tworzenia nowego obiektu gniazda - datagramy będą odbierane domyślnie na porcie na którym były poprzednio wysyłane. Konstruktor bezparametrowy stosowany jest najczęściej po stronie aplikacji klienta. public DatagramSocket() konstruktor bezparametrowy. Adres i port docelowy zdefiniowane są w obiekcie DatagramPacket wysyłanym przez gniazdo. Po utworzeniu gniazda można wysyłać datagramy metodą send: byte buf[] = new byte[100]; // bufor wielkości 100 bajtów... // umieszczenie danych w buforze DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getByName("kos.man.koszalin.pl"), 13 ); DatagramSocket socket= new DatagramSocket(); // obiekt gniazda socket.send(packet); // wysłanie paczki (datagramu) 2.2.2. Gniazda odbierające dane Konstruktory stosowany jest po stronie aplikacji serwera muszą być przywiązane do numeru portu: public DatagramSocket(int port) konstruktor gniazda z parametrem: port - numer portu wiązanego z gniazdem - typu int. Gniazdo zostaje wiązane z portem numer port UDP. Drugi z konstruktorów dla gniazd odbierających dane ma możliwość związania z wybranym adresem przypisanym do hosta. Stosowany dla hostów posiadających więcej niż jeden adres IP. public DatagramSocket(int port, InetAddress addr) konstruktor gniazda z parametrami: port - numer portu wiązanego z gniazdem - typu int, addr - obiekt klasy InetAddress opisujący wiązany z gniazdem adres IP. Gniazdo zostaje wiązane z portem numer port UDP i adresem IP opisywanym przez addr. Po utworzeniu gniazda można odbierać datagramy metodą receive: - 5 -

byte buf[] = new byte[100]; // bufor wielkości 100 bajtów DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.receive( packet ); // oczekiwanie na datagram Metoda receive oczekuje na przyjście datagramu podobnie jak metoda accept klasy ServerSocket (komunikacja TCP) i także jest jest to metoda blokująca. W sytuacji gdy gniazdo nie powinno oczekiwać w nieskończoność na dane przychodzące można ustawić czas oczekiwania gniazda metodą setsotimeout. Kod utworzenia gniazda datagramowego wymaga obsługi odpowiednich wyjątków. Konstruktory klasy mogą zwrócić wyjątki: SocketException - gdy wystąpi błąd otwarcia gniazda; SecurityException - wyjątek ochrony (wyrzucany przez obiekt SecurityManager, np. w przeglądarce internetowej). 3. Przykłady realizacji usług opartych na UDP W dalszej części przedstawione są przykłady rozwiązań komunikacji opartej na UDP. Największym problemem w tego typu komunikacji jest zapewnienie minimum niezawodności wymiany informacji i jest to zadanie niezwykle złożone. Przedstawione tu przykłady nie zapewniają pełnej niezawodności, są jedynie prezentacją kilku rozwiązań. 3.1. Komunikacja jednokierunkowa W najprostrzych rozwiązaniach komunikacji opartej na UDP realizuje się komunikację jednokierunkową gdzie jedna strona (klient) wysyła dane, druga (serwer) odbiera dane (Rys. 2). Warte podkreślenia jest to że strona wysyłająca w żaden sposób nie jest w stanie stwierdzić czy wysłane dane dotarły do celu. Takie rozwiązanie można zastosować gdy dotarcie wszystkich danych do adresata nie jest konieczne (dźwięk, obraz itp.). Zaletą tak prostego systemu komunikacji jest szybkość transferu danych. host klienta host serwera SendDatagram packet ReceiveDatagram packet socket send() Datagramy od klienta do serwera (w przykładzie klient wysyła 10 datagramów) socket receive() Rys. 2. Jednokierunkowe przesłanie datagramów (bez potwierdzenia!) - 6 -

Przedstawione przykłady - aplikacje SendDatagram, ReceiveDatagram - tworzą prosty system komunikacji jednokierunkowej. Aplikacja SendDatagram wysyła 10 datagramów zawierających datę i godzinę w odstępach jedno-sekundowych. Aplikacja ReceiveDatagram odbiera wszystkie datagramy przychodzące do gniazda i drukuje informacje na konsoli. Przykład 1. SendDatagram.java import java.net.*; import java.util.*; public class SendDatagram { public static void main(string args[]) { int port = 5500; for (int i =0; i<10; i++) { // petla wysyla 10 datagramow try { InetAddress servaddr = InetAddress.getByName("192.168.55.43"); String data = i + ". Data i godzina wyslania: " + new Date(); byte buf[] = data.getbytes(); DatagramSocket socket= new DatagramSocket(); DatagramPacket packet = new DatagramPacket(buf, buf.length, servaddr, port ); socket.send(packet); System.out.println("Wyslano datagram"); /* Zatrzymanie na 1s */ try { Thread.sleep(1000); catch (InterruptedException ex) {System.err.println("Blad sleep()!"); ; // try catch(exception ex) { System.err.println(ex); // for // main // SendDatagram Przykład 2. ReceiveDatagram.java import java.net.*; public class ReceiveDatagram { public static void main(string args[]){ int port = 5500; System.out.println("Aplikacja ReceiveDatagram czeka na datagramy: port " + port); try{ DatagramSocket socket = new DatagramSocket(port); while(true) { byte[] buf = new byte[100]; DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.receive( packet ); System.out.println("Dane odebrane [" + new String(packet.getData()).trim() + "]" ); // while // try catch(exception e) { System.err.println(e); - 7 -

// main // ReceiveDatagram 3.2. Proste usługi (przykład realizacji usługi daytime) Większość usług opartych na UDP, realizowanych w praktyce, są to proste usługi działające na zasadzie zapytanie-odpowiedź. Wśród standardowych usług udostępnianych na serwerach sieciowych można wyróżnić: echo (port 7), discard (port 9), daytime (port 13), time (port 37), name (port 42),... itd. Wszystkie te usługi wysyłają datagram informujący serwer usługi o żądaniu realizacji usługi. W niektórych usługach wymagane jest skonstruowanie odpowiedniego zapytania przez klienta (np. name), w innych klient wysyła datagram pusty (daytime, time - dane zawarte w datagramie są bez znaczenia). Usługa diagnostyczna echo przyjmuje dowolne dane i odsyła je w takiej postaci w jakiej przyszły. host klienta host serwera DatagramClient packet DatagramServer packet socket send() receive() 1. Datagram od klienta do serwera - datagram pusty 2. odpowiedź serwera np. "Wed Sep 11 18:53:51 2002" socket receive() send() Rys. 3. Realizacja prostych usług zapytanie-opdpowiedź Przykładowa aplikacja DatagramClient realizuje funkcję klienta usługi daytime wysyłając datagram pusty. Zmiana portu na 9 lub 37 zmieni funkcję aplikacji odpowiednio na realizację usług discard i time. Prosta modyfikacja zawartości danych w wysyłanym datagramie pozwoliłaby na realizację usług echo, name. Przykład 3. DatagramClient.java import java.net.*; import java.io.*; public class DatagramClient { public static void main(string args[]) { try{ InetAddress servaddr = InetAddress.getByName("localhost"); /* wys³anie pustego datagramu */ byte buf[] = new byte[1]; DatagramSocket socket= new DatagramSocket(); socket.send(new DatagramPacket(buf, buf.length, servaddr, 13 )); System.out.println("Wyslano datagram: [" + new String(buf).trim() + "]"); /* odbior odpowiedzi */ - 8 -

buf = new byte[100]; DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.setsotimeout(2000); socket.receive(packet); System.out.println("Odebrano datagram: [" + new String(buf).trim() + "]"); // try catch(unknownhostexception e) { System.err.println("Jakis blad - UnknownHostEx"); catch(socketexception e) { System.err.println("Jakis blad - SocketEx"); catch(ioexception e) { System.err.println("Jakis blad - IOEx"); // main // DatagramClient Przykład 4 przedstawia implementację serwera usługi daytime. Realizacja usługi zrealizowana jest w pętli nieskończonej while: while(true){ /* Odebranie datagramu */... socket.receive( packet );... /* Wyslanie datagramu z data i godzina */ socket.send( new DatagramPacket( buf, buf.length, packet.getaddress(), packet.getport() ) );... Według podobnego schematu działają serwery innych, wcześniej wymienionych usług i dostosowanie serwera do obsługi innych usług (podobnie jak w przypadku klienta) nie stanowi większego problemu. Przykład 4. DatagramServer.java import java.net.*; import java.util.*; import java.io.*; public class DatagramServer{ public static void main(string args[]){ try{ DatagramSocket socket = new DatagramSocket(13); while(true){ /* Odebranie pustego datagramu */ byte[] buf = new byte[100]; DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.receive( packet ); System.out.println("Odebrano datagram: [" + new String(buf).trim() + "]"); /* Wyslanie datagramu z data i godzina */ buf = new Date().toString().getBytes(); socket.send( new DatagramPacket( buf, buf.length, packet.getaddress(), packet.getport() ) ); System.out.println("Odeslano datagram: [" + new String(buf).trim() + "]"); // while // try catch(unknownhostexception e) { System.err.println("Jakis blad - UnknownHostEx"); catch(socketexception e) { System.err.println("Jakis blad - SocketEx"); catch(ioexception e) { System.err.println("Jakis blad - IOEx"); // main - 9 -

// DatagramServer 3.3. Przesyłanie większych bloków danych (przykład usługi copyfile) Przykład aplikacji CopyFileServer realizuje przesłanie zawartości pliku do klienta z podziałem pliku na kawałki tzw. chunks (ang.: chunk - kawałek) o maksymalnej wielkości 8192 bajty (zalecany rozmiar bloku danych w datagramie). Podobne rozwiązanie było stosowane w kilku usługach sieciowych lecz zostało wyparte protokołami opartymi na TCP (np. usługa TFTP). W obecnych czasach można spotkać transfer pliku podzielonego na kawałki w różnych specyficznych sieciach komunikacyjnych m.in. w systemach typu P2P (peer to peer). Poniżej prezentowane rozwiązanie trudno nazwać rozwiązaniem niezawodnym z uwagi na brak kilku istotnych zabezpieczeń. Między innymi pominięta została kontrola źródła przychodzących datagramów, kontrola kolejności przychodzących kawałków. Aplikacja serwera oczekująca na porcie 5501 wykonuje działania: odbiera pusty datagram - zgłoszenie klienta, wysyła datagram zawierający prosty komunikat "OK" - potwierdzenie dla klienta, oblicza ilość kawałków na które zostanie podzielony plik, odczytuje w pętli for kolejne kawałki z pliku, wysyła odczytany kawałek w postaci datagramu, odczytuje potwierdzenie od klienta - datagram pusty, wysyła kolejne kawałki aż do wyczerpania zawartości pliku i odbierając potwierdzenia od klienta po każdym kawałku. Przykład 5. CopyFileServer.java import java.net.*; import java.io.*; public class CopyFileServer { int port = 5501; FileInputStream file; int filesize; String filename; private boolean openfile(string filename) { try { file = new FileInputStream(filename); filesize = file.available(); // wielkosc pliku catch (FileNotFoundException ex) { System.out.println("Nie znaleziono pliku!"); return false; catch (IOException ex) { System.out.println("Blad odczytu!"); return false; return true; private void closefile() { try { if (file!= null) file.close(); catch (IOException ex) { System.out.println("Blad przy zamykaniu pliku!"); public CopyFileServer(String localfile) { - 10 -

System.out.println("Startuje serwer CopyFile na porcie " + port); filename = localfile; // nazwa pliku try{ DatagramSocket socket = new DatagramSocket(port); // gniazdo serwera na porcie while (true) { /* odczyt zgloszenia - datagram pusty */ byte[] buf = new byte[100]; // bufor do odczytu datagramu DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.receive( packet ); // oczekiwanie na datagram System.out.println("Zgloszenie od klienta..."); /* wyslanie potwierdzenia "OK" */ if (openfile(filename)) buf = "OK".getBytes(); else System.exit(0); socket.send(new DatagramPacket(buf, buf.length, packet.getaddress(), packet.getport())); /* wyslanie kolejnych kawalkow */ int chunks = (int)(filesize/8192); for (int i =0 ; i <= chunks; i++) { byte[] buffer; if (filesize-i*8192 >= 8192) buffer = new byte[8192]; else buffer = new byte[filesize-i*8192]; int count = file.read(buffer); System.out.println("Wysylam kawalek: " + count + " " + (i+1)+ "/" + (chunks+1)); socket.send( new DatagramPacket( buffer, buffer.length, packet.getaddress(), packet.getport())); socket.receive( packet ); // odbior potwierdzenia - datagram pusty if (file!= null) closefile(); // zamkniecie pliku // while // try catch(unknownhostexception e) { System.err.println("Jakis blad - UnknownHostEx"); catch(socketexception e) { System.err.println("Jakis blad - SocketEx"); catch(ioexception e) { System.err.println("Jakis blad - IOEx"); public static void main(string args[]){ if (args.length > 0 ) { new CopyFileServer(args[0]); else { System.out.println("Poprawne uruchomienie serwera:"); System.out.println("java CopyFileServer <nazwa pliku>"); // main // DatagramServer Aplikacja klienta wykonuje działania: wysyła pusty datagram - zgłoszenie do serwera, odbiera datagram zawierający prosty komunikat "OK" - potwierdzenie od serwera, oblicza ilość kawałków na które zostanie podzielony plik, odbiera w pętli for kolejne datagramy przychodzące od serwera, wysyła potwierdzenie do serwera - datagram pusty, odbiera kolejne kawałki aż do wyczerpania zawartości pliku za każdym razem potwierdzając datagramem pustym. Przykład 5. CopyFileClient.java import java.net.*; - 11 -

import java.io.*; public class CopyFileClient { int port = 5501; FileOutputStream file; int filesize; String filename; private boolean openfile(string filename) { try { file = new FileOutputStream(filename); catch (FileNotFoundException ex) { System.out.println("Nieznaleziono pliku!"); return false; catch (IOException ex) { System.out.println("Blad odczytu!"); return false; return true; private void closefile() { try { if (file!= null) file.close(); catch (IOException ex) { System.out.println("Blad przy zamykaniu pliku!"); public CopyFileClient(String localfile, int size) { filename = localfile; filesize = size; // parametry pliku: nazwa i rozmiar try{ /* zgloszenie uslugi - datagram pusty */ InetAddress servaddr = InetAddress.getByName("localhost"); byte buf[] = " ".getbytes(); DatagramSocket socket= new DatagramSocket(); socket.setsotimeout(3000); // timeout dla gniazda 3 sekundy socket.send(new DatagramPacket(buf, buf.length, servaddr, port )); /* odbior potwierdzenia przyjecia zgloszenia */ buf = new byte[100]; DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.receive(packet); String answer = new String(packet.getData()).trim(); System.out.println("Informacja od serwera: " + answer); if (!answer.equals("ok")) { System.out.println("Blad po stronie serwera! Koniec"); System.exit(0); /* otwarcie pliku do zapisu */ openfile(filename); int chunks = (int)(filesize/8192); // ilosc wysylanych kawalkow for (int i = 0 ; i <= chunks; i++) { byte[] buffer; // bufor datagramu - maksymalny rozmiar kawalka 8192 if (filesize < 8192) buffer = new byte[filesize-i*8192]; else if (filesize-i*8192 >= 8192) buffer = new byte[8192]; else buffer = new byte[filesize-i*8192]; packet = new DatagramPacket(buffer, buffer.length); - 12 -

socket.receive(packet); // odbior kawalka file.write(packet.getdata()); // zapis do pliku socket.send(new DatagramPacket(new byte[1], 1, servaddr, 5501 )); // wyslanie potwierdzenia // for // try catch(unknownhostexception e) { System.err.println("Jakis blad - UnknownHostEx"); catch(socketexception e) { System.err.println("Jakis blad - SocketEx"); catch(ioexception e) { System.err.println("Jakis blad - IOEx"); finally { if (file!= null) closefile(); // zamkniecie pliku // konstruktor /* program glowny main*/ public static void main(string args[]){ new CopyFileClient("joke.jpg", 212385); /* program g³ówny koniec main*/ // CopyFileClient Zaprezentowane rozwiązanie jest jedynie demonstracją możliwości protokołu UDP i zastosowanie go w praktyce może okazać się niewystarczające. Do kopiowania większych bloków danych stosuje się częściej rozwiązania pozwalające na przesyłanie kawałków w kolejności losowej (w UDP nie ma gwarancji zachowania kolejności przychodzących datagramów) lecz takie rozwiązanie wymaga zastosowania systemu oznaczeń kawałków (numerowania) i systemu buforowania danych. - 13 -

3. Aplety i komunikacja sieciowa Aplet to specyficzny rodzaj aplikacji javy, której podstawową cechą jest działanie pod kontrolą maszyny wirtualnej java wbudowanej w inną aplikację. Taką aplikacją posiadającą własną, wbudowaną maszynę wirtualną może być np. popularna przeglądarka internetowa Netscape, MS Internet Explorer, przeglądarka appletviewer dołączana do JDK Sun itd. Ze względu na inny rodzaj środowiska pracy aplet powinien spełnić określone warunki bezpieczeństwa. W środowisku maszyny wirtualnej wbudowanej w przeglądarkę pracuje obiekt SecurityManager kontrolujący prawa dostępu do zasobów systemu. W zastosowaniach komunikacji sieciowej SM kontroluje prawa do połączeń sieciowych. Standardowo dopuszczane są jedynie próby połączeń z hostem z którego pobrany został aplet. Rys. 4. Połączenia sieciowe apletu W sytuacji gdy aplet jest uruchamiany lokalnie z lokalnego pliku html (bez serwera www) obiekt SecurityManager nie dopuści do żadnych połączeń, również do połączeń lokalnych. Większość omawianych wcześniej klas (konstruktory tych klas) wyrzucają wyjątek SecurityException w przypadku niedozwolonej próby połączenia. Jest to wynikiem działania obiektu SecurityManager. Aplikacje - aplety uruchamiane pod kontrolą przeglądarki powinny obsługiwać ten wyjątek przy tworzeniu obiektów gniazd. - 14 -

5. Podsumowanie UDP-TCP W tabeli 1 zestawione zostały najważniejsze cechy komunikacji opartej na TCP i UDP. Warto podkreślić że obydwa protokoły posiadają wady i zalety. Zadaniem programisty jest określenie potrzeb zależnych od zastosowań i wybór odpowiedniego rozwiązania. Tabela 1. Najważniejsze cechy połączeń opartych na TCP i UDP Gniazda TCP Gniazda UDP utrzymują połączenie potwierdzenie realizowane na poziomie protokołu TCP bez udziału programisty dane przychodzące w kolejności wysłania możliwa komunikacja aktywnego gniazda tylko z jednym klientem (lub serwerem) mniejsza szybkość komunikacji spowodowana koniecznością wymiany dodatkowych informacji i działań (potwierdzeń, zwalnianie szybkości, buforowanie itp.) brak połączenia potwierdzenia realizowane przez programistę na poziomie aplikacji dane (datagramy) mogą docierać do adresata w innej kolejności niż kolejność wysłania możliwe odbieranie datagramów od wielu klientów na pojedynczym gnieździe większa szybkość spowodowana brakiem dodatkowych informacji Na podstawie porównania z tabeli można wysnuć wnioski: realizacja usługi opartej na protokole TCP jest korzystna gdy potrzebna jest stabilność komunikacji, wszystkie dane muszą dotrzeć do adresata, szybkość komunikacji ma mniejsze znaczenie; realizacja usługi opartej na protokole UDP jest korzystna gdy większe znaczenie ma szybkość działania (przekazania danych), nie jest niezbędne dostarczenie wszystkich danych. Dodatek. Ćwiczenia do samodzielnego wykonania Poniżej przedstawione są zadania do wykonania na ćwiczeniach lub do samodzielnego wykonania w zaciszu domowym. 1. Skompilować wszystkie przykłady (od 1 do 9). Można to wykonać z linii poleceń OS, z pomocą pliku wsadowego, lub z wykorzystaniem środowiska RealJ zainstalowanego na salach komputerowych. 2. Dla przykładu 1 (SendDatagram) i 2 (ReceiveDatagram): uruchomić na lokalnym stanowisku aplikację ReceiveDatagram (serwer) i aplikację SendDatagram (klient) tak aby aplikacje mogły się komunikować; zestawić połączenie pomiędzy dwoma stanowiskami wszystkie wyniki zanotować. 3. Dla przykładu 3 (DatagramClient) i 4 (DatagramServer): - 15 -

uruchomić na lokalnym stanowisku aplikację DatagramClient, odczytać datę i godzinę na hostach: moskit.ie.tu.koszalin.pl, kos.man.koszalin.pl, innych wybranych hostach np. microsoft.com, wp.pl (usługi te mogą nie być dostępne lub czas odpowiedzi może być długi - ustawić timeout); zestawić połączenie pomiędzy dwoma stanowiskami: na jednym stanowisku pracujący serwer na drugim klient; wyniki wszystkich działań zanotować. 4. Dla przykładu 5 (CopyFileServer) i 6 (CopyFileClient): zestawić połączenie pomiędzy dwoma stanowiskami i skopiować wybrany plik (w prezentowanym przykładzie klient ma założony z góry rozmiar kopiowanego pliku - wartość tą należy ustawić dla własnego pliku), wyniki zanotować. - 16 -