Programowanie aplikacji w architekturze Klient-Serwer - TCP



Podobne dokumenty
Komunikacja w sieci Industrial Ethernet z wykorzystaniem Protokołu S7 oraz funkcji PUT/GET

System Informatyczny CELAB. Przygotowanie programu do pracy - Ewidencja Czasu Pracy

VLAN Ethernet. być konfigurowane w dowolnym systemie operacyjnym do ćwiczenia nr 6. Od ćwiczenia 7 należy pracować ć w systemie Linux.

API transakcyjne BitMarket.pl

Opis zmian funkcjonalności platformy E-GIODO wprowadzonych w związku z wprowadzeniem możliwości wysyłania wniosków bez podpisu elektronicznego

Aplikacja wielow tkowa prosty komunikator

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

PERSON Kraków

Dziedziczenie : Dziedziczenie to nic innego jak definiowanie nowych klas w oparciu o już istniejące.

Systemy mikroprocesorowe - projekt

VinCent Office. Moduł Drukarki Fiskalnej

GEO-SYSTEM Sp. z o.o. GEO-RCiWN Rejestr Cen i Wartości Nieruchomości Podręcznik dla uŝytkowników modułu wyszukiwania danych Warszawa 2007

Instrukcja postępowania w celu podłączenia do PLI CBD z uwzględnieniem modernizacji systemu w ramach projektu PLI CBD2

CGI i serwlety. Plan wykładu. Wykład prowadzi Mikołaj Morzy. Przykład: serwlety vs. szablony. Implementacja logiki prezentacji

Sieci komputerowe. Definicja. Elementy

Zainstalowana po raz pierwszy aplikacja wymaga aktualizacji bazy danych obsługiwanych sterowników.

Microsoft Management Console

WYMAGANIA EDUKACYJNE I KRYTERIA OCENIANIA Z PRZEDMIOTU PROGRAMOWANIE APLIKACJI INTERNETOWYCH

InsERT GT Własne COM 1.0

Warszawa, r.

Wdrożenie modułu płatności eservice dla systemu Virtuemart 2.0.x

Akademickie Centrum Informatyki PS. Wydział Informatyki PS

POLITYKA PRYWATNOŚCI SKLEPU INTERNETOWEGO

Automatyzacja procesu publikowania w bibliotece cyfrowej

REGULAMIN INTERNETOWEJ OBSŁUGI KLIENTA

Kancelaris - Zmiany w wersji 2.50

epuap Ogólna instrukcja organizacyjna kroków dla realizacji integracji

Zarządzanie Zasobami by CTI. Instrukcja

Regu g l u a l min i n w s w pó p ł ó p ł r p acy O ow o iązuje od dnia

Zdalne odnawianie certyfikatów do SWI

Harmonogramowanie projektów Zarządzanie czasem

System kontroli wersji SVN

Postanowienia ogólne. Usługodawcy oraz prawa do Witryn internetowych lub Aplikacji internetowych

Bazy danych II. Andrzej Grzybowski. Instytut Fizyki, Uniwersytet Śląski

SKRÓCONA INSTRUKCJA OBSŁUGI ELEKTRONICZNEGO BIURA OBSŁUGI UCZESTNIKA BADANIA BIEGŁOŚCI

8. Konfiguracji translacji adresów (NAT)

Budowa systemów komputerowych

Chmura obliczeniowa. do przechowywania plików online. Anna Walkowiak CEN Koszalin

Opis obsługi systemu Ognivo2 w aplikacji Komornik SQL-VAT

Instrukcja programu PControl Powiadowmienia.

emszmal 3: Automatyczne księgowanie przelewów w sklepie internetowym Magento (plugin dostępny w wersji ecommerce)

Spis treści INTERFEJS (WEBSERVICES) - DOKUMENTACJA TECHICZNA 1

Sieci komputerowe cel

ANALOGOWE UKŁADY SCALONE

SpedCust 5 instrukcja instalacji

Politechnika Warszawska Wydział Matematyki i Nauk Informacyjnych ul. Koszykowa 75, Warszawa

Rozwiązywanie nazw w sieci. Identyfikowanie komputerów w sieci

Wtedy wystarczy wybrać właściwego Taga z listy.

Platforma do obsługi zdalnej edukacji

elektroniczna Platforma Usług Administracji Publicznej

Archiwum Prac Dyplomowych

Ewidencja abonentów. Kalkulacja opłat

Poniżej instrukcja użytkowania platformy

Praca na wielu bazach danych część 2. (Wersja 8.1)

Spis treści. Rozdział 1 ewyniki. mmedica - INSTR UKC JA UŻYTKO W NIKA

Elementy cyfrowe i układy logiczne

REGULAMIN. przeprowadzania naboru nowych pracowników do korpusu służby cywilnej w Kuratorium Oświaty w Szczecinie.

Oprogramowanie FonTel służy do prezentacji nagranych rozmów oraz zarządzania rejestratorami ( zapoznaj się z rodziną rejestratorów FonTel ).

WYMAGANIA OFERTOWE. Przetarg nr PZ-451

Komunikacja z użyciem gniazd aplikacje klient-serwer

Adres strony internetowej, na której Zamawiający udostępnia Specyfikację Istotnych Warunków Zamówienia:

Rozliczenia z NFZ. Ogólne założenia. Spis treści

Zarządzanie projektami. wykład 1 dr inż. Agata Klaus-Rosińska

2.1 INFORMACJE OGÓLNE O SERII NX

Technologie internetowe Internet technologies Forma studiów: Stacjonarne Poziom kwalifikacji: I stopnia. Liczba godzin/tydzień: 2W, 2L

INSTRUKCJA TESTOWANIA USŁUG NA PLATFORMIE ELA-ENT

Tytuł pracy. Praca dyplomowa inżynierska. Filip Piechocki. Tytuł Imię i Nazwisko

Regulamin serwisu internetowego ramowka.fm

KLAUZULE ARBITRAŻOWE

REGULAMIN PRZESYŁANIA I UDOSTĘPNIANIA FAKTUR W FORMIE ELEKTRONICZNEJ E-FAKTURA ROZDZIAŁ 1. I. Postanowienia ogólne

System do kontroli i analizy wydawanych posiłków

Tworzenie wielopoziomowych konfiguracji sieci stanowisk asix z separacją segmentów sieci - funkcja POMOST. Pomoc techniczna

Pracownia internetowa w każdej szkole (edycja Jesień 2007)

INSTRUKCJA RUCHU I EKSPLOATACJI SIECI DYSTRYBUCYJNEJ

PRZEMYSŁOWY ODTWARZACZ PLIKÓW MP3 i WAV

Elementy podłączeniowe.

Przekształcenie danych przestrzennych w interaktywne mapy dostępne na stronach www (WARSZTATY, poziom podstawowy)

zgubił całą naszą korespondencję Można by tak wymieniać bez bezpieczeństwa, gdyby była wykonana dnia poprzedniego rozwiązałaby niejeden problem.

Regulamin Projektów Ogólnopolskich i Komitetów Stowarzyszenia ESN Polska

Adapter USB do CB32. MDH-SYSTEM ul. Bajkowa 5, Lublin tel./fax lub kom e mail: info@mdh-system.pl

Konfiguracja przeglądarek internetowych oraz Panelu Java dla klientów instutucjonalnych problemy z apletem do logowania/autoryzacji

Linux LAMP, czyli Apache, Php i MySQL

INSTRUKCJA DO PROGRAMU LICZARKA 2000 v 2.56

Załącznik nr 8. Warunki i obsługa gwarancyjna

Instrukcja obsługi. Oprogramowanie SAS 31

Elementy animacji sterowanie manipulatorem

MUltimedia internet Instrukcja Instalacji

Seria P-662HW-Dx. Bezprzewodowy modem ADSL2+ z routerem. Skrócona instrukcja obsługi

Oprogramowanie klawiatury matrycowej i alfanumerycznego wyświetlacza LCD

Instrukcja obsługi platformy zakupowej e-osaa (klient podstawowy)

Konfiguracja OpenVPN

Obowiązuje od 30 marca 2015 roku

Konfigurator opisuje proces instalacji i konfiguracji karty sieciowej bezprzewodowej D-Link DWL-520+ w systemach /2000/XP /

Specyfikacja usługi CCIE R&S

Instalacja. Zawartość. Wyszukiwarka. Instalacja Konfiguracja Uruchomienie i praca z raportem Metody wyszukiwania...

Logowanie do mobilnego systemu CUI i autoryzacja kodami SMS

Projektowanie bazy danych

Regulamin Obrad Walnego Zebrania Członków Stowarzyszenia Lokalna Grupa Działania Ziemia Bielska

Transkrypt:

Katedra Inżynierii Komputerowej Koszalin 2002 Programowanie dla sieci Wykłady i ćwiczenia Dariusz Rataj Część 1 Programowanie aplikacji w architekturze Klient-Serwer - TCP 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. Wstęp - podstawowe pojęcia... 2 2. Architektura Klient-Serwer... 4 3. Interfejs Gniazd (Sockets API).... 5 3. 1. Reprezentacja adresu IP (klasa pomocnicza - InetAddress)... 6 3. 2. Budowa aplikacji klienta - protokół TCP (klasa Socket)... 8 Przykład aplikacji skanera portów... 9 Przykład aplikacji klienta usługi daytime... 11 Przykład aplikacji klienta usługi http... 12 3. 3. Budowa aplikacji serwera - protokół TCP (klasa ServerSocket)... 14 Przykład aplikacji serwera - serwer iteracyjny... 16 Przykład aplikacji serwera - serwer współbieżny... 18 3.4. Przykład systemu komunikacji - prosty Chat... 21 3.4.1. Program serwera... 21 3.4.2. Program klienta... 23 Dodatek. Ćwiczenia do samodzielnego wykonania... 25 1. Wstęp - podstawowe pojęcia W celu usystematyzowania tworzenia i pracy sieci przyjęto model opisujący zależności poszczególnych elementów sieci. Jest to model OSI (Open System Interconnection) i jest to norma komunikacji komputerowej ISO (Rys. 1). Model ten dzieli zadania sieci na siedem warstw z których każda posiada konkretne zadania do wykonania. 7. Warstwa zastosowań 6. Warstwa prezentacji 5. Warstwa sesji 4. Warstwa transportowa 3. Warstwa sieciowa 2. Warstwa kanałowa 1. Warstwa fizyczna Rys. 1. Siedmiowarstwowy model sieci OSI Każda z warstw sieci posiada własne protokoły komunikacyjne. Natomiast grupę (zbiór) protokołów pracujących na różnych warstwach nazywamy rodziną protokołów. Sieć Internet oparta jest na rodzinie protokołów TCP/IP. Pomiędzy protokołami różnych warstw tworzy się tzw. interfejsy umożliwiające współpracę pomiędzy warstwami. W większości zastosowań wystarczy nam znajomość wersji modelu uproszczonego OSI - modelu czterowarstwowego (Rys. 2), w którym warstwy 5-7 i 1-2 zostały połączone. - 2 -

Połączone warstwy 5-7 nazywamy warstwą procesu (lub warstwą aplikacji). Połączone warstwy 1-2 nazywamy warstwą kanałową (lub warstwą fizyczną). Uproszczony model przedstawiony na jest jedynie ogólnie przyjętym schematem i może wyglądać inaczej (inne niestandardowe protokoły w poszczególnych warstwach) w zależności od systemu operacyjnego i konfiguracji systemu. Dla potrzeb programisty znajomość tego modelu jest wystarczająca. Warstwa aplikacji (procesu) (warstwy od 5 do 7 modelu OSI) SMTP TELNET RLOGIN FTP RPC INNE USŁUGI TCP Warstwa transportowa (warstwa 4 modelu OSI) UDP Warstwa sieciowa (warstwa 3 modelu OSI) Internet Protocol (IP) Warstwa kanałowa (fizyczna) (warstwy 1 i 2 modelu OSI) Sieć lokalna Sieć rozległa Rys. 2. Uproszczony czterowarstwowy model sieci - rodzina protokołów TCP/IP Dla celów programistycznych największe znaczenie w tym modelu mają protokoły TCP i UDP mające bezpośredni wpływ na wykorzystywane komponenty podczas procesu programowania zastosowań sieciowych. TCP (Transmission Control Protocol) - protokół umożliwiający połączenie logiczne dwóch urządzeń w sieci i transfer danych (potoku bajtów) w dwie strony z gwarancją dostarczenia danych do adresata. O takim protokole mówimy że jest to protokół z kontrolą połączenia. UDP (User Datagram Protocol) - protokół ten nie realizuje połączenia umożliwiającego transfer w dwie strony tak jak w przypadku TCP. Informacja jest przesyłana w formie datagramów bez gwarancji ich dostarczenia. O takim protokole mówimy że jest to protokół bez kontroli połączenia. Większość "popularnych" protokołów (usług) powyżej warstwy transportowej (http, ftp, telnet itd.) korzysta z protokołu TCP (z oczywistych powodów: konieczność utrzymania połączenia, potwierdzenie dostarczenia danych). Powyżej warstwy transportowej wszystkie usługi są jednoznacznie identyfikowane przez przydzielone numery zwane portami. Chcąc zrealizować połączenie należy znać dwa parametry: adres IP hosta (komputera), numer portu przydzielonego dla usługi, - 3 -

Numer portu jest wartością całkowitą zawierającą się w przedziale 1-65535. Niektóre usługi używają typowych, stałych numerów portów, identycznych dla wszystkich komputerów w sieci. Numery portów z zakresu 1-1023 zostały zarezerwowane dla tych właśnie usług systemowych i nie należy (chociaż jest to możliwe) ich wykorzystać w aplikacjach obsługi usług innych niż systemowe (standardowe). Także nie uda się przejęcie portu wykorzystywanego już przez inną usługę. 2. Architektura Klient-Serwer Architektura Klient-Serwer opiera się na istnieniu dwóch stron (dwóch procesów) biorących udział w połączeniu: Klient żądający dostępu do usługi, Serwer udostępniający usługę w sieci. Funkcje realizowane przez klienta można opisać: połączenie ze zdalnym hostem (przygotowanie do odbioru i wysyłania danych), odbiór danych, wysyłanie danych, zamknięcie połączenia. Funkcje realizowane przez serwer można ująć następująco: przywiązanie portu (zarezerwowanie portu dla danej usługi na hoście), nasłuch (oczekiwanie na zgłoszenie klienta), realizacja połączenia z klientem (akceptacja połączenia), wysyłanie danych, odbiór danych, zamknięcie połączenia. Na rysunku 3 przedstawiony został schemat połączenia w architekturze Klient-Serwer. Klient Ścieżka logiczna Serwer Warstwa aplikacji Warstwa aplikacji Warstwa transportowa (TCP,UDP) Warstwa transportowa (TCP,UDP) Warstwa sieciowa (IP) Warstwa sieciowa (IP) Warstwa kanałowa (fizyczna) Ścieżka fizyczna Rys. 3. Realizacja połączenia w architekturze Klient-Serwer Fizyczna realizacja wymiany danych jest procesem skomplikowanym składającym się z wielu operacji: warstwa aplikacji komunikuje się z warstwą transportową (TCP,UDP), warstwa transportowa komunikuje się z warstwą sieciową (IP), warstwa sieciowa komunikuje się z - 4 -

warstwą fizyczną (np. Ethernet). Po stronie serwera kolejność przepływu danych jest odwrotna. Z punktu widzenia programisty fizyczna ścieżka połączenia ma mniejsze znaczenie. Programista dysponuje jedynie interfejsem aplikacji i widzi jedynie swój komputer. Takie połączenie z pominięciem pośrednich warstw nazwano połączeniem logicznym. Realizację (programowanie) połączenia logicznego realizuje się wykorzystując odpowiedni interfejs programowy API (Application Program Interface). Podstawowym API wykorzystywanym do programowania zastosowań sieciowych jest interfejs gniazdek (Sockets API). 3. Interfejs Gniazd (Sockets API). Interfejs gniazd pojawił się wraz z systemem UNIX BSD w wersji 4.1c w roku 1982 i szybko stał się podstawowym narzędziem obsługi zastosowań sieciowych. Obecnie interfejs gniazdek dostępny jest w większości systemów operacyjnych w postaci bibliotek lub jako element jądra systemu. Sockets API dostępny jest w większości środowisk programistycznych różnych języków programowania (C, C++, Java, Pascal, Delphi itd.). Gniazdka stanowią API czyli kolejną warstwę-interfejs pomiędzy warstwą aplikacji i warstwą transportową (Rys. 4). Część systemu widoczna z punktu widzenia programisty Aplikacja klienta Aplikacja serwera Interfejs gniazdek Interfejs gniazdek Warstwa transportowa (TCP,UDP) Warstwa transportowa (TCP,UDP) Pozostałe warstwy sieci Rys. 4. Interfejs gniazdek w architekturze Klient-Serwer Od początku istnienia języka Java - Sockets API jest podstawowym pakietem obsługi sieci w tym języku. W języku Java nie mamy dostępu do warstw niższych (IP, Ethernet) sieci przez co programista jest "zmuszony" do korzystania z interfejsów wyższych warstw. W standardowych bibliotekach zawartych w JRE (Java Runtime) firmy SUN zbiór klas obsługi sieci umieszczony jest w pakiecie java.net. W skład pakietu wchodzą m.in.: InetAddress - klasa reprezentująca adres IP; Socket,ServerSocket - klasy używające do komunikacji protokołu TCP; DatagramPacket, DatagramSocket, MulticastSocket - klasy używające do komunikacji protokołu UDP; URL,URLConnection - używające do komunikacji protokołu TCP. - 5 -

Wymienione powyżej klasy są to jedynie podstawowe klasy pakietu java.net realizujące podstawowe zadania pakietu. W skład pakietu wchodzą również klasy pomocnicze i interfejsy obsługi wyjątków, klasy związane z autoryzacją. 3. 1. Reprezentacja adresu IP (klasa pomocnicza - InetAddress) Klasa InetAddress jest obiektową reprezentacją adresu IP hosta. Umożliwia uzyskanie podstawowych informacji związanych z adresami: adresu lokalnego hosta, nazwy lokalnego hosta, adresu i nazwy zdalnego hosta. Klasa ta nie posiada publicznych konstruktorów i utworzenie obiektu możliwe jest dzięki metodom statycznym klasy: - public static InetAddress InetAddress.getLocalHost() metoda zwraca obiekt klasy InetAddress opisujący lokalny host (host na którym uruchomiono aplikację); - public static InetAddress InetAddress.getByName(String host) metoda zwraca obiekt klasy InetAddress opisujący zdalny host. Jako parametr host można użyć zarówno adres IP w postaci tekstowej (np. "153.19.134.100") jak i nazwy hosta (np. "termit.ie.tu.koszalin.pl"); - public static InetAddress[] InetAddress.getAllByName(String host) metoda zwraca tablicę obiektów typu InetAddress. Niektóre nazwy w DNS mają przypisany więcej niż jeden adres IP. Tablica obiektów zwróci informacje o wszystkich adresach IP (jeden adres w obiekcie typu InetAddress w zwracanej tablicy). Przeważnie nazwy posiadające kilka adresów IP są to hosty o dużym obciążeniu wymagające różnych adresów dla wielu realizowanych funkcji. Jako parametr podawana jest nazwa hosta (np. "kos.man.koszalin.pl"). Po utworzeniu obiektu klasy InetAddress można skorzystać z dwóch metod zwracających informacje o adresie: - public String gethostname() metoda zwraca nazwę hosta opisywanego przez obiekt; - public String gethostaddress() metoda zwraca adres IP hosta w postaci tekstowej opisywanego przez obiekt. Przykładowa aplikacja (Przykład 1) prezentuje pobranie informacji na temat hosta lokalnego (metoda getlocalhost()) oraz hosta zdalnego (metoda getbyname() z parametrami nazwa hosta i adres IP). Przykład 1. demoinetaddress.java import java.net.*; class demoinetaddress { public static void main (String[] args) { - 6 -

// pobranie danych lokalnego hosta InetAddress adres1 = InetAddress.getLocalHost(); System.out.println("Nazwa lokalnego hosta: " + adres1.gethostname()); System.out.println("Adres lokalnego hosta: " + adres1.gethostaddress() + "\n"); // pobranie danych hosta o znanej nazwie InetAddress adres2 = InetAddress.getByName("moskit.ie.tu.koszalin.pl"); System.out.println("Nazwa: " + adres2.gethostname()); System.out.println("Adres: " + adres2.gethostaddress() + "\n"); // pobranie danych hosta o znanym adresie IP InetAddress adres3 = InetAddress.getByName("194.181.178.33"); System.out.println("Nazwa: " + adres3.gethostname()); System.out.println("Adres: " + adres3.gethostaddress() + "\n"); catch (UnknownHostException e) { System.err.println(e); // koniec main // koniec demoinetaddress Wynik działania aplikacji demoinetaddress (host termit): termit> java demoinetaddress Nazwa lokalnego hosta: termit.ie.tu.koszalin.pl Adres lokalnego hosta: 153.19.134.100 Nazwa: moskit.ie.tu.koszalin.pl Adres: 194.181.179.33 Nazwa: kos.man.koszalin.pl Adres: 194.181.178.33 Przykład getalladdresses (Przykład 2) pobiera wszystkie adresy IP przypisane do nazwy podanej jako parametr aplikacji wykorzystując metodę InetAddress.getAllByName(host). Pobrane adresy (obiekty) zapisywane są w tablicy obiektów adresy. Przykład 2. getalladdresses.java import java.net.*; class getalladdresses { public static void main (String[] args) { String host = "kos.man.koszalin.pl"; if (args.length > 0) host = args[0]; System.out.println("Adresy przypisane do nazwy " + host + ":\n"); InetAddress[] adresy = InetAddress.getAllByName(host); for (int i = 0; i < adresy.length; i++) { System.out.println(adresy[i]); catch (UnknownHostException ex) { System.err.println("Nie moge znalezc hosta " + host + " :(!"); - 7 -

// koniec catch // koniec main // koniec getalladdresses Wynik działania aplikacji getalladdresses: D:\java\examples\java getalladdresses kos.man.koszalin.pl Adresy przypisane do nazwy kos.man.koszalin.pl: kos.man.koszalin.pl/194.181.178.33 kos.man.koszalin.pl/153.19.134.23 --------------------------------------------------------------------------------------- D:\java\examples\java getalladdresses ibm.com Adresy przypisane do nazwy ibm.com: ibm.com/129.42.19.99 ibm.com/129.42.16.99 ibm.com/129.42.17.99 ibm.com/129.42.18.99 3. 2. Budowa aplikacji klienta - protokół TCP (klasa Socket) Klasa Socket jest wykorzystywana przede wszystkim do budowy aplikacji klienta (aplikacji korzystającej z usługi) jako główna klasa tworząca połączenie z serwerem (połączenie TCP). Klasa ta wykorzystywana jest także po stronie serwera do komunikacji z klientem. Obiekt klasy Socket realizuje podstawowe zadania: zgłoszenie klienta do serwera, utrzymanie połączenia, zamknięcie połączenia. Klasa Socket posiada szereg (sześć) publicznych konstruktorów z czego dwa są najczęściej stosowane: - public Socket(String host, int port) konstruktor gniazda z parametrami: host - nazwa lub adres IP hosta serwera w postaci tekstowej typu String, port - numer portu usługi działającej na serwerze typu int; - public Socket(InetAddress address, int port) konstruktor gniazda z parametrami: address - obiekt klasy InetAddress reprezentujący adres serwera, port - numer portu usługi działającej na serwerze typu int. Kod utworzenia gniazdka wymaga obsługi odpowiednich wyjątków. Konstruktor klasy może zwrócić wyjątki: UnknownHostException - w sytuacji gdy nazwa lub IP hosta są nieznane; IOException - gdy wystąpi błąd operacji wejścia-wyjścia. Konstruktor może zwócić także wyjątek SecurityException w sytuacji gdy istnieje działający obiekt ochrony zasobów (security manager) i nie dopuszcza do połączenia. Obsługę SecurityException należy realizować w apletach. Security Manager maszyny wirtualnej wbudowanej w przeglądarkę internetową kontroluje prawa do połączenia i blokuje niektóre z nich. Przykładowy fragment poprawnie utworzonego gniazda klienta z obsługą wyjątków może wyglądać następująco: - 8 -

socket = new Socket("kos.man.koszalin.pl", 13);... catch (UnknownHostException ex) { System.err.println(ex); catch (IOException e) { System.err.println(ex); Po poprawnym utworzeniu obiektu gniazdka i ustanowieniu połączenia z serwerem należy utworzyć obiekty strumieni umożliwiające komunikację z serwerem. Służą do tego dwie metody klasy Socket: - public InputStream getinputstream() metoda zwraca obiekt wejściowy klasy InputStream służący jako strumień odczytu danych przychodzących z serwera. - public OutputStream getoutputstream() metoda zwraca obiekt wyjściowy klasy OutputStream służący jako strumień zapisu (wysłania) danych do serwera. W praktyce obiekty typu InputStream i OutputStream poddawane są często konwersji na inne typy strumieni. Wykonuje się to z uwagi na to że klasy te posiadają bardzo ograniczone możliwości zapisu i odczytu danych (niewielka ilość metod) i są niewygodne w użyciu. Strumień wejściowy konwertowany jest na obiekt-strumień klasy BufferedReader: BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); Dzięki takiej konwersji można wykorzystać metodę readline() odczytującą wartość typu String z gniazdka zamiast trudnych w obsłudze metod klasy InputStream. Strumień wyjściowy zamieniany jest na strumień-obiekt klasy PrintWriter: PrintWriter out = new PrintWriter(socket.getOutputStream(), true); Dzięki takiej konwersji strumienia OutputStream można wykorzystać szereg przeciążonych metod print(...) i println(...) klasy PrintWriter przeznaczonych do wysyłania (pisania) do gniazdka danych różnych typów. W celu zakończenia połączenia należy wywołać metodę close() na rzecz obiektu gniazdka. Jedynie w niektórych aplikacjach nie jest to niezbędne - w tych, w których czas życia obiektu gniazdka i działanie aplikacji kończy się wraz z ostatnią operacją na otwartym połączeniu (np.: przykładowa aplikacja daytime). Przykład aplikacji skanera portów Przykładowa aplikacja skanera portów (Przykład 3) wykonuje badanie możliwość realizacji połączenia na portach 1-1023. Jeżeli połączenie przebiega poprawnie na konsoli drukowany jest komunikat o działającym serwerze. W przypadku braku połączenia wywoływana jest - 9 -

obsługa wyjątka IOException. W tym konkretnym przypadku nie jest to traktowane jako błąd (nic się nie dzieje). Sprawdzanie portów odbywa się w pętli for gdzie następuje próba utworzenia obiektu klasy Socket z parametrami host (nazwa, ip hosta) oraz i (numer portu). W przypadku udanej próby połączenia, po wydruku informacji na konsoli, połączenie zostaje zamknięte metodą close(). Przedstawiony tu przykład prostego skanera portów jest niestety mało efektywny. Próba skanowania wykonana na serwerze moskit.ie.tu.koszalin.pl trwała około 15 minut co w praktyce jest nie do przyjęcia. Przyspieszenie działania takich rozwiązań można osiągnąć budując aplikację wielowątkową gdzie skanowanie odbywać się będzie na kilku portach jednocześnie (w kilku lub kilkunastu wątkach). Przykład 3. scannerports.java import java.net.*; import java.io.*; public class scannerports { public static void main(string[] args) { Socket socket; String host = "termit.ie.tu.koszalin.pl"; if (args.length > 0) host = args[0]; System.out.println("Skanuje porty hosta: " + host); for (int i = 1; i < 1024; i++) { socket = new Socket(host, i); System.out.println("na porcie " + i + " dziala serwer"); socket.close(); catch (UnknownHostException e) { System.err.println(e); break; catch (IOException e) { // brak serwera na tym porcie - nic sie nie dzieje // koniec for // koniec main // koniec skanerports Wynik działania aplikacji scannerports: D:\java\examples\java scannerports moskit.ie.tu.koszalin.pl Skanuje porty hosta: moskit.ie.tu.koszalin.pl na porcie 7 dziala serwer na porcie 9 dziala serwer na porcie 13 dziala serwer na porcie 19 dziala serwer na porcie 21 dziala serwer na porcie 22 dziala serwer na porcie 23 dziala serwer na porcie 25 dziala serwer na porcie 37 dziala serwer - 10 -

na porcie 79 dziala serwer na porcie 80 dziala serwer na porcie 110 dziala serwer na porcie 111 dziala serwer na porcie 113 dziala serwer na porcie 174 dziala serwer na porcie 512 dziala serwer na porcie 513 dziala serwer na porcie 514 dziala serwer na porcie 540 dziala serwer Przykład aplikacji klienta usługi daytime Przykład 4 przedstawia implementację aplikacji prostego klienta usługi daytime. Daytime jest standardową usługą działającą na większości serwerów internetowych zwracającą bieżącą datę i godzinę na serwerze. Rysunek 5 wyjaśnia działanie komunikacji klienta z serwerem. host klienta host serwera daytimeclient socket in 1. zgłoszenie (żądanie) połączenia 2. odpowiedź serwera np. "Wed Sep 11 18:53:51 2002" Aplikacja obsługująca usługę serwer usługi (port nr 13) Rys. 5. Schemat działania aplikacji daytimeclient Usługa daytime jest przykładem najprostszej usługi możliwej do realizacji w sieci przy użyciu protokołu TCP. Od strony aplikacji klienta usługa ta realizowana jest w dwóch czynnościach: - zgłoszenie (żądanie) połączenia z serwerem usługi, - odebranie odpowiedzi od serwera tj. daty i godziny w postaci łańcucha tekstowego. Po wykonaniu tych działań połączenie zostaje zakończone (serwer zamyka połączenie). Przykład 4. daytimeclient.java import java.net.*; import java.io.*; public class daytimeclient { public static void main(string[] args) { Socket socket; String host = "kos.man.koszalin.pl"; BufferedReader in; - 11 -

if (args.length > 0) host = args[0]; /* tworzenie gniazda klienta, pobranie danych, wydruk informacji na konsoli */ socket = new Socket(host, 13); in = new BufferedReader( new InputStreamReader(socket.getInputStream())); String time = in.readline(); System.out.println("Host: " + host + " zwraca na porcie nr 13:"); System.out.println("date i czas: " + time); catch (UnknownHostException e) { System.err.println(e); catch (IOException e) { System.err.println(e); // koniec main // koniec daytimeclient Wynik działania aplikacji daytimeclient: D:\java\examples\java daytimeclient moskit.ie.tu.koszalin.pl Host: moskit.ie.tu.koszalin.pl zwraca na porcie nr 13: date i czas: Sun Sep 22 14:11:48 2002 Przykład aplikacji klienta usługi http W odróżnieniu od aplikacji daytime klient usługi http realizuje dwustronną wymianę danych z serwerem (Rys. 6). Od strony aplikacji klienta usługa http realizowana jest w trzech etapach: - zgłoszenie (żądanie) połączenia z serwerem usługi, - wysłanie komunikatu (zapytania) zgodnego z protokołem http, - odebranie odpowiedzi od serwera zawierającej nagłówek oraz zawartość dokumentu (dokumentu html, grafiki, muzyki itd.). host klienta host serwera httpclient socket out in 1. zgłoszenie (żądanie) połączenia 2. Wysłanie komunikatu (zapytania) zgodnego z http 3. Odpowiedź serwera zgodna z http (nagłówek + dokument) Aplikacja obsługująca usługę serwer usługi www (port nr 80) np. Apache Rys. 6. Schemat działania aplikacji httpclient - 12 -

W przykładzie 5 program klienta wysyła komunikat do serwera zapisany w zmiennej message typu String. Zmienna message zawiera proste zapytanie zgodne z protokołem http - żądanie o domyślny dokument (najczęściej index.html) umieszczony w katalogu głównym serwera http: "GET / HTTP/1.1" Jest to żądanie (ang. request) typu GET standardowo wysyłane przez przeglądarki internetowe po wpisaniu adresu w polu adresów przeglądarki. Dla odróżnienia wysyłając wypełniony formularz ze strony www przeglądarka wysyła żądanie typu POST. Pozostałe linie zapytania są to dodatkowe informacje dla serwera: formaty odbieranych dokumentów, rodzaj połączenia, adres strony polecającej, informacje o przeglądarce, adres hosta klienta, format dokumentu itp. Dodatkowe parametry mogą być różne dla różnych przeglądarek i mogą, ale nie muszą, być wymagane przez serwer. Po odebraniu żądania przez serwer wysyłana jest odpowiedź: nagłówek odpowiedzi oraz dokument (w tym przykładzie zawartość dokumentu index.html z serwera termit). Pierwsza linia nagłówka ("HTTP/1.1 200 OK") informuje o poprawnym przebiegu połączenia. W innym przypadku w tej linii znajdzie się informacja o błędzie. W dalszej części nagłówka znajdują się dodatkowe parametry odpowiedzi. Najważniejsze z nich to Content-Length zawierający długość dokumentu w bajtach oraz Content-Type informujący o formacie dokumentu. Nagłówek jest oddzielony od dokumentu linią pustą. Przykład 5. httpclient.java import java.io.*; import java.net.*; public class httpclient { // pobranie domyslnego dokumentu w glownym katalogu serwera http (/) // najczesciej index.html String message = "GET / HTTP/1.1\r\n"+ "Accept: */*\r\n" + // */ "Connection: close\r\n"+ "Referer: moskit.ie.tu.koszalin.pl\r\n"+ "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; IDG.pl)\r\n"+ "Host: moskit.ie.tu.koszalin.pl\r\n"+ "Content-Type: text/html\r\n"; void getdocument() { Socket socket; BufferedReader in; String tekst = "", all = ""; PrintWriter out = null; socket = new Socket("termit.ie.tu.koszalin.pl", 80); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new PrintWriter(socket.getOutputStream(), true); out.println(message); System.out.println("---------------- Komunikat http wyslany --------------------"); System.out.println("------------------ Odpowiedz serwera: ----------------------"); while( (tekst=in.readline())!=null){ System.out.println(tekst); in.close(); socket.close(); - 13 -

catch (UnknownHostException e) { System.err.println("Blad URL: sprawdz poprawnosc url"); catch (IOException e) { System.err.println("Blad I/O"); System.out.println("------------------- Koniec odpowiedzi ----------------------"); public httpclient() // konstruktor { getdocument(); public static void main(string args[]) { new httpclient(); // utworzenie obiektu klienta Wynik działania aplikacji httpclient - odwołanie do serwera http na hoście termit: D:\java\examples\java httpclient ---------------- Komunikat http wyslany -------------------- ------------------ Odpowiedz serwera: ---------------------- HTTP/1.1 200 OK Date: Wed, 11 Sep 2002 19:25:12 GMT Server: Apache/1.3.23 (Unix) PHP/4.1.2 Last-Modified: Fri, 28 Jul 2000 07:58:55 GMT ETag: "229a4-68-39813d3f" Accept-Ranges: bytes Content-Length: 104 Connection: close Content-Type: text/html <html> <body> <meta http-equiv="refresh" CONTENT="0;URL=http://www.ie.tu.koszalin.pl"> </body> </html> 3. 3. Budowa aplikacji serwera - protokół TCP (klasa ServerSocket) Zadaniem programu serwera jest realizacja usługi na rzecz klienta. Program serwera rozpoczyna pracę w systemie komputerowym po czym zasypia i oczekuje na zgłoszenie klienta zamawiającego jego usługę. W języku Java funkcję serwera połączeń z potwierdzeniem (TCP) realizują przede wszystkim obiekty klasy ServerSocket. Obiekt klasy ServerSocket nasłuchuje na przydzielonym porcie do momentu nadejścia zgłoszenia po czym budzi się i rozpoczyna realizację usługi. Aplikacja Javy (aplikacja wielowątkowa) może utworzyć kilka obiektów klasy ServerSocket a tym samym postawić klika serwerów czuwających na różnych portach (na jednym hoście, na każdym porcie może działać jeden i tylko jeden serwer!). - 14 -

Serwery możemy podzielić na dwie podstawowe grupy: 1. serwery iteracyjne - obsługujące w danej chwili tylko jednego klienta. Kolejne zgłoszenia klientów oczekują w kolejce na połączenie z serwerem. Takie rozwiązania są najprostsze w realizacji lecz dla wielu zastosowań niewystarczające. Serwery iteracyjne buduje się dla prostych usług, w których czas obsługi usługi jest krótki i znany z góry. Jest to rozwiązanie stosowane również w specyficznych rozwiązaniach, w których serwer komunikuje się jedynie z jednym stałym klientem. Przykładem prostej usługi, w której z góry możemy określić ilość danych wysyłanych do klienta jest usługa daytime w której ilość danych przesyłanych jest niewielka i stała (przykład 4); 2. serwery współbieżne - realizowane w sytuacji gdy czas obsługi usługi może być różny w zależności od żądań klienta a także gdy serwer ma obsłużyć wyjątkowo dużo zgłoszeń od klientów. Serwer współbieżny (w Javie obiekt ServerSocket), po ustanowieniu połączenia, tworzy nowy proces (wątek) do wykonania zamówień klienta po czym ponownie zasypia w oczekiwaniu na kolejne zgłoszenie. Przykładami takich serwerów mogą być serwery usług ftp, http itd. Klasa ServerSocket posiada cztery publiczne konstruktory: - public ServerSocket() bezparametrowy konstruktor gniazda serwera. Utworzenie obiektu gniazda przy użyciu tego konstruktora nie wiąże gniazda z portem (obiekt nie jest gotowy do użycia). Wiązanie należy wykonać metodą bind; - public ServerSocket(int port) konstruktor gniazda serwera z parametrem port - numer portu wiązanego z serwerem typu int; - public ServerSocket(int port, int backlog) konstruktor gniazda z parametrami: port - numer portu wiązanego z serwerem typu int, backlog - długość kolejki oczekujących klientów (gdy ilość oczekujących klientów przekroczy tą wartość kolejne zgłoszenia będą odrzucane); - public ServerSocket(int port, int backlog, InetAddress bindaddr) konstruktor gniazda z parametrami: port - numer portu wiązanego z serwerem typu int, backlog - długość kolejki oczekujących klientów (gdy ilość oczekujących klientów przekroczy tą wartość kolejne zgłoszenia będą odrzucane) typu int, bindaddr - lokalny adres wiązany z serwerem usługi (parametr stosowany dla hostów posiadających kilka adresów, gniazdo będzie przyjmować zgłoszenia do jednego z nich) typu InetAddress. Kod utworzenia gniazdka serwera wymaga obsługi odpowiednich wyjątków. Konstruktor klasy może zwrócić wyjątki: IOException - gdy wystąpi błąd operacji wejścia-wyjścia; SecurityException - wyjątek ochrony (patrz: konstruktory klasy Socket). Przykładowy fragment poprawnie utworzonego gniazda serwera z obsługą wyjątków może wyglądać następująco: - 15 -

ServerSocket server = new ServerSocket(80);... catch (IOException e) { System.out.println("Blad utworzenia gniazda"); W dalszej części przedstawione zostaną dwa przykłady - serwer iteracyjny i serwer współbieżny realizujące usługę http. W celu skrócenia kodów źródłowych serwery realizują odpowiedź serwera niezależną od komunikatów klienta (zawsze zwracają tą samą odpowiedź). Aby zwiększyć funkcjonalność przykładowych serwerów należy dołączyć metody analizy tekstu przysyłanych żądań i obsługi plików. Przykład aplikacji serwera - serwer iteracyjny Schemat działania aplikacji jhttpserver, prostego serwera http, przedstawiono na rysunku 7. Aplikacja realizuje funkcję serwera iteracyjnego - przyjmuje zgłoszenie klienta, realizuje usługę. Kolejny klient żądający połączenia oczekuje w kolejce na realizację połączenia. host serwera Aplikacja obsługująca usługę Klient usługi Klient usługi Realizowane połączenie Zgłoszenie oczekujące na zakończenie aktualnie realizowanego połączenia serwer iteracyjny (port 80) Rys. 7. Działanie serwera iteracyjnego jhttpserver Realizacja usługi zrealizowana została w nieskończonej pętli for: while (true) { socket = server.accept(); // uśpienie serwera... // tutaj obsługa usługi, pobranie strumieni z gniazda... // odebranie i wysłanie odpowiedzi Serwer przechodzi w stan uśpienia (za pomocą metody accept) po czym budzi się i realizuje usługę. Po zakończeniu ponownie wchodzi w stan uśpienia. Jeżeli kolejny klient zgłosił wcześniej żądanie (czeka w kolejce) serwer ponownie budzi się i realizuje usługę. Realizacja usługi w tym przykładzie polega na odebraniu komunikatu (zapytania) od klienta i wysłania odpowiedzi (zasada działania usługi http). Serwer można przetestować przy użyciu dowolnej przeglądarki internetowej (Rys. 9) podając adres localhost (dla serwera uruchamianego lokalnie). - 16 -

Rys. 9. Wygląd dokumentu zwracanego przez jhttpserver w przeglądarce Dla uproszczenia kodu źródłowego serwer odbiera jedynie pierwszą linię żądania klienta. Serwer wysyła do klienta odpowiedź wygenerowaną przez metodę getanswer. Metoda ta zawiera dwie zmienne lokalne typu String: document, header. Odpowiedź serwera składana jest z tych dwóch zmiennych oddzielonych pustą linią (pusta linia jest separatorem oddzielającym nagłówek odpowiedzi od dokumentu - jest podstawowa informacja dla przeglądarki i jest niezbędna!): return header + "\r\n\r\n" + document; Niektóre fragmenty odpowiedzi serwera są (muszą być) generowane dynamicznie. W nagłówku odpowiedzi parametr Content-Length jest długością dokumentu w bajtach i jest wyznaczany metodą length klasy String. Dokument zwraca bieżącą datę i godzinę (obiekt klasy Date), nazwę i adres hosta. Przykład 6. jhttpserver.java import java.net.*; import java.io.*; import java.util.*; public class jhttpserver { private int port = 80; String getanswer() { InetAddress adres; String name = ""; String ip = ""; adres = InetAddress.getLocalHost(); name = adres.gethostname(); - 17 -

ip = adres.gethostaddress(); catch (UnknownHostException e) { System.err.println(e); String document = "<html>\r\n" + "<body><br>\r\n" + "<h2><font color=red>jhttpserver demo document\r\n" + "</font></h2>\r\n" + "<h3>server iteracyjny</h3><hr>\r\n" + "Data: <b>" + new Date() + "</b><br>\r\n" + "Nazwa hosta: <b>" + name + "</b><br>\r\n" + "IP hosta: <b>" + ip + "</b><br>\r\n" + "<hr>\r\n" + "</body>\r\n" + "</html>\r\n"; String header = "HTTP/1.1 200 OK\r\n" + "Server: jhttpserver ver 1.1\r\n" + "Last-Modified: Fri, 28 Jul 2000 07:58:55 GMT\r\n" + "Content-Length: " + document.length() + "\r\n" + "Connection: close\r\n" + "Content-Type: text/html"; return header + "\r\n\r\n" + document; public jhttpserver(int port){ this.port = port; Socket socket = null; ServerSocket server = new ServerSocket(port); while (true) { socket = server.accept(); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); System.out.println("---------------- Pierwsza linia zapytania ----------------"); System.out.println(in.readLine()); System.out.println("---------------- Wysylam odpowiedz -----------------------"); System.out.println(getAnswer()); System.out.println("---------------- Koniec odpowiedzi -----------------------"); out.println(getanswer()); out.flush(); if (socket!= null) socket.close(); catch (IOException e) { System.out.println("Blad otwarcia"); public static void main(string[] args) { new jhttpserver(80); Przykład aplikacji serwera - serwer współbieżny Serwer jhttpserver z przykładu 6 został rozbudowany w przykładzie 7 (jhttpapp) o możliwość przetwarzania współbieżnego (wielowątkowego). Działanie serwera od strony - 18 -

klienta wygląda identycznie jak w serwerze iteracyjnym (dane zwracane do przeglądarki są podobne). Zgodnie ze schematem działania z rysunku 8 uśpiony serwer oczekujący na zgłoszenie budzi się po czym tworzy nowy wątek aplikacji i przekazuje wątkowi realizację usługi. host serwera Klient usługi Klient usługi Realizowane połączenie Realizowane połączenie Aplikacja obsługująca usługę wątek 1 wątek 2 Klient usługi Nowe zgłoszenie klienta wątek-serwer oczekujący na kolejne zgłoszenie (accept) Rys. 8. Działanie serwera współbieżnego W aplikacji jhttpapp obiekt-serwer klasy ServerSocket czuwa na porcie 80 (metoda accept). Po zgłoszeniu klienta obiekt socket klasy Socket przekazywany jest jako parametr konstruktora klasy jhttpsmulti. Klasa ta dziedziczy po klasie Thread (ang. wątek) przeznaczonej do pracy jako dodatkowy wątek aplikacji. ServerSocket server = new ServerSocket(80); while (true) { Socket socket = server.accept(); // uśpienie serwera new jhttpsmulti(socket); // nowy obiekt-wątek aplikacji // while // try finally { server.close(); Obsługa klienta realizowana jest już tylko w obiekcie klasy jhttpsmulti natomiast serwer ponownie wchodzi w stan uśpienia (nieskończona pętla while). Realizację usługi wykonuje metoda run klasy jhttpsmulti wywołana pośrednio z poziomu konstruktora metodą start. Po wymianie danych z klientem działanie metody run kończy się a tym samym kończy się działanie obiektu klasy jhttpsmulti (obiekt przestaje istnieć). Przykład 7. jhttpapp.java import java.net.*; import java.io.*; import java.util.*; class jhttpsmulti extends Thread{ private Socket socket = null; String getanswer() { - 19 -

InetAddress adres; String name = ""; String ip = ""; adres = InetAddress.getLocalHost(); name = adres.gethostname(); ip = adres.gethostaddress(); catch (UnknownHostException ex) { System.err.println(ex); String document = "<html>\r\n" + "<body><br>\r\n" + "<h2><font color=red>jhttpapp demo document\r\n" + "</font></h2>\r\n" + "<h3>server wspó³bie ny</h3><hr>\r\n" + "Data: <b>" + new Date() + "</b><br>\r\n" + "Nazwa hosta: <b>" + name + "</b><br>\r\n" + "IP hosta: <b>" + ip + "</b><br>\r\n" + "<hr>\r\n" + "</body>\r\n" + "</html>\r\n"; String header = "HTTP/1.1 200 OK\r\n" + "Server: jhttpserver ver 1.1\r\n" + "Last-Modified: Fri, 28 Jul 2000 07:58:55 GMT\r\n" + "Content-Length: " + document.length() + "\r\n" + "Connection: close\r\n" + "Content-Type: text/html; charset=iso-8859-2"; return header + "\r\n\r\n" + document; public jhttpsmulti(socket socket){ System.out.println("Nowy obiekt jhttpsmulti..."); this.socket = socket; start(); public void run() { PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); System.out.println("---------------- Pierwsza linia zapytania ----------------"); System.out.println(in.readLine()); System.out.println("---------------- Wysylam odpowiedz -----------------------"); System.out.println(getAnswer()); System.out.println("---------------- Koniec odpowiedzi -----------------------"); out.println(getanswer()); out.flush(); catch (IOException e) { System.out.println("Blad IO danych!"); finally { if (socket!= null) socket.close(); catch (IOException e) { System.out.println("Blad zamkniecia gniazda!"); // finally - 20 -

public class jhttpapp { public static void main(string[] args) throws IOException { ServerSocket server = new ServerSocket(80); while (true) { Socket socket = server.accept(); new jhttpsmulti(socket); // while // try finally { server.close(); // main 3.4. Przykład systemu komunikacji - prosty Chat Poniżej przedstawiony zostanie przykład systemu komunikacyjnego realizującego połączenie zgodne z modelem klient-serwer wykorzystującego klasy ServerSocket (po stronie serwera) i Socket (po stronie klienta). Aplikacje zrealizują prosty Chat (wymianę danych tekstowych) pomiędzy dwoma hostami. Możliwa będzie jedynie wymiana informacji na przemian (czytanie, pisanie, czytanie, pisanie,...) z jednym klientem. Połączenie Chat z wykorzystaniem serwera iteracyjnego przedstawia rysunek 9. host klienta host serwera SimpleClient socket 1. zgłoszenie (żądanie) połączenia SimpleSerwer serversocket in out Dane od klienta do serwera Dane od serwera do klienta socket out in Rys. 9. Komunikacja systemu Chat - serwer iteracyjny W bardziej rozbudowanych programach typu Chat realizowana jest współbieżna (wielowątkowa) praca serwera a także wielowątkowa praca klienta (czytanie z serwera, pisanie do serwera). Przykład Chat został wykorzystany jako przykład własnego systemu komunikacyjnego. W aplikacjach wymiany dźwięku, obrazu, danych numerycznych zasady realizacji komunikacji są identyczne. 3.4.1. Program serwera Najważniejszym elementem aplikacji serwera jest obiekt klasy ServerSocket, którego zadaniem jest udostępnienie portu (tutaj port nr 4444) aplikacji klienta i zestawienie połączenia. Zainicjowanie obiektu ServerSocket serversocket = new ServerSocket(4444); rezerwuje port wykorzystywany przez usługę. - 21 -

Metoda accept() klasy ServerSocket powoduje aktywowanie usługi: oczekiwanie na zgłoszenie klienta i zestawienie połączenia z klientem. clientsocket = serversocket.accept(); Metoda accept() zwraca obiekt - gniazdko identyfikujące klienta. Od tego momentu komunikacja z klientem odbywa się przez ten właśnie obiekt (tutaj clientsocket) natomiast obiekt serversocket nie jest używany. Komunikacja z klientem odbywa się za pośrednictwem strumieni (wejściowego i wyjściowego) pobranych z gniazdka klienta. Wysyłanie danych do klienta odbywa się przez obiekt strumień out typu PrintWriter: PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); Odbieranie danych od klienta odbywa się przez obiekt strumień in typu BufferedReader: BufferedReader in = new BufferedReader( new InputStreamReader(clientSocket.getInputStream())); Wymiana danych między serwerem i klientem wykonywana jest na przemian (pisanie przez serwer, czytanie klienta, pisanie przez serwer, czytanie klienta,...) w pętli: while ((fromclient = in.readline())!= null) { System.out.println("Client: " + fromclient);... fromserver = stdin.readline(); // pobranie danych z konsoli... out.println(fromserver); // wyslanie danych do klienta... Wyjście z pętli następuje w sytuacji gdy klient przyśle polecenie 'exit', użytkownik serwera wyda polecenie 'quit', metoda in.readline() nie odczyta danych od klienta. W sytuacji gdy użytkownik serwera wyda polecenie 'exit' komunikat ten zostanie wysłany do klienta - klient zakończy działanie co spowoduje brak danych zwróconych przez in.readline(), natomiast serwer ponownie wejdzie w stan oczekiwania (czuwania) na zgłoszenie klienta (metoda accept()). Podobnie stanie się gdy podane zostanie polecenie 'quit' serwera z tą różnicą że serwer nie wejdzie w stan oczekiwania i nastąpi zakończenie pracy serwera. Przejście w stan ponownego oczekiwania na zgłoszenie klienta realizowane jest w pętli "nieskończonej" - while(true) {... w której umieszczone są wszystkie wyżej wymienione operacje. Wyjście z tej pętli realizowane jest tylko przez polecenie 'quit' wydane przez użytkownika serwera. Po zakończeniu pracy klienta i serwera konieczne jest zamknięcie odpowiednich strumieni klienta i serwera metodami close(). Przykład 8. SimpleServer.java import java.net.*; import java.io.*; public class SimpleServer { public static void main(string[] args) throws IOException { String fromclient="", fromserver=""; ServerSocket serversocket = null; // gniazdko serwera // zainicjowanie gniazdka na porcie nr 4444 serversocket = new ServerSocket(4444); - 22 -

catch (IOException e) { System.err.println("Nie moge oczekiwac na porcie 4444!"); System.exit(1); BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); while (true) { Socket clientsocket = null; // gniazdko klienta System.out.println("Jestem Serwer:"); System.out.println(" 'exit' - wyrzucenie Klienta i ponowne oczekiwanie"); System.out.println(" 'quit' - zakonczenie pracy"); System.out.println("Oczekiwanie na polaczenie..."); // wlaczenie nasluchu i ew. nawiazanie polaczenia clientsocket = serversocket.accept(); catch (IOException e) { System.err.println("Blad accept"); System.exit(1); // pobranie strumienia wyjsciowego klienta PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); // pobranie strumienia wejsciowego klienta BufferedReader in = new BufferedReader( new InputStreamReader(clientSocket.getInputStream())); while ((fromclient = in.readline())!= null) // petla odczytu i zapisu do gniazdka { System.out.println("Client: " + fromclient); // wydruk na konsoli danych klienta if (fromclient.equals("exit")) { // zakonczenie przez klienta System.out.println("Client sie odlaczyl!!! "); break; if (fromserver.equals("quit")) break; // zakonczenie przez serwer System.out.print("Server: "); fromserver = stdin.readline(); // pobranie danych z konsoli if (fromserver!= null) { out.println(fromserver); // wyslanie danych do klienta out.close(); // zamkniecie otwartych strumieni i gniazdka in.close(); clientsocket.close(); if (fromserver.equals("quit")) break; // zakonczenie przez serwer stdin.close(); // zamkniecie strumienia z konsoli i gniazdka serwera serversocket.close(); 3.4.2. Program klienta Aplikacja klient rozpoczyna działanie od zainicjowania gniazdka - zainicjowania obiektu (tutaj socket) klasy Socket. W konstruktorze klasy Socket podajemy dwa parametry adres hosta - 23 -

serwera i port usługi (te dwa parametry ) jednoznacznie identyfikują program-serwer oczekujący na zgłoszenie klienta. socket = new Socket("10.10.10.2", 4444); Jeżeli połączenie się powiodło konstruktor zwraca obiekt - gniazdko identyfikujące połączenie. Od tego momentu komunikacja z serwerem odbywa się przez ten właśnie obiekt (socket). Komunikacja z serwerem odbywa się za pośrednictwem strumieni (wejściowego i wyjściowego) pobranych z gniazdka socket. Wysyłanie danych do serwera odbywa się przez obiekt strumień out typu PrintWriter: PrintWriter out = new PrintWriter(socket.getOutputStream(), true); Odbieranie danych od serwera odbywa się przez obiekt strumień in typu BufferedReader: BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); Zasada wymiany danych za pośrednictwem strumieni jest podobna jak w przypadku aplikacji serwera i wykonywana jest na przemian (czytanie danych z serwera, pisanie przez klienta, czytanie danych z serwera, pisanie przez klienta,...) w pętli: while ((fromserver = in.readline())!= null) { // wydruk na konsoli danych z serwera System.out.println("Server: " + fromserver);... fromuser = stdin.readline(); // pobranie danych z konsoli... out.println(fromuser); // wyslanie danych do serwera Wyjście z pętli (zakończenie działania klienta) nastąpi w sytuacji gdy z serwera zostanie przysłany komunikat 'exit' (wyłączenie klienta przez serwer) lub komunikat 'quit' (zakonczenie pracy serwera). Jeżeli użytkownik klienta wyda polecenie 'exit' zostanie ono wysłane do serwera i serwer zakończy działanie klienta. Identycznie jak w aplikacji serwera na zakończenie pracy klienta należy zamknąć wszystkie otwarte strumienie. Przykład 9. SimpleClient.java import java.io.*; import java.net.*; public class SimpleClient { public static void main(string[] args) throws IOException { Socket socket = null; // gniazdko PrintWriter out = null; // strumien wyjsciowy - dane wysylane na server BufferedReader in = null; // strumien wejsciowy - dane czytane z servera String fromserver, fromuser; // zainicjowanie gniazdka socket = new Socket("192.168.66.5", 4444); System.out.println("Czekam na wiadomosc z Servera..."); // pobranie strumienia wyjsciowego serwera - 24 -

out = new PrintWriter(socket.getOutputStream(), true); // pobranie strumienia wejsciowego serwera in = new BufferedReader(new InputStreamReader(socket.getInputStream())); catch (UnknownHostException e) { System.err.println("Nieznany adres hosta"); System.exit(1); catch (IOException e) { System.err.println("Blad operacji Wejscia/Wyjscia - I/O error"); System.exit(1); // stdin - wejœcie standardowe (wej. konsoli java) BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); out.println("jest na linii! Pisz Serwerze!!!"); while ((fromserver = in.readline())!= null) // petla odczytu i zapisu do gniazdka { System.out.println("Server: " + fromserver); // wydruk na konsoli danych z serwera if (fromserver.equals("exit") fromserver.equals("quit")) // zakonczenie przez serwer { System.out.println("Zostalem wyrzucony przez Server!"); break; System.out.print("Client: "); fromuser = stdin.readline(); // pobranie danych z konsoli if (fromuser!= null) { out.println(fromuser); // wyslanie danych do serwera out.close(); // zamkniecie otwartych strumieni i gniazdka in.close(); stdin.close(); socket.close(); 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: uruchomić aplikację i zanotować wyniki (nazwy i adresy); zmodyfikować program i sprawdzić adresy hostów: wbiis.tu.koszalin.pl, puma.tu.koszalin.pl, microsoft.com; zmodyfikować program i sprawdzić nazwy hostów: 192.168.55.20, 148.81.247.7, 153.19.134.114; wszystkie wyniki zanotować. 3. Dla przykładu 2: - 25 -

zbadać adresy IP dla nazw: ibm.com, microsoft.com, java.sun.com, kos.man.koszalin.pl; zanotować wyniki. 4. Dla przykładu 3: wykonać skanowanie portów sąsiada (sąsiedniego komputera na sali); nie skanować (!) serwerów uczelnianych (moskit, termit itd.); zanotować wyniki. 5. Dla przykładu 4: pobrać datę i czas z hostów: moskit.ie.tu.koszalin.pl, puma.tu.koszalin.pl, kos.man.koszalin.pl, termit.ie.tu.koszalin.pl; zmodyfikować program zmieniając nr portu na 80 (http) i uruchomić (Uwaga! jeżeli nie jest ustawiony timeout dla gniazda aplikacja będzie czekać bez końca - ustawić timeout); zanotować wyniki. 6. Dla przykładu 5: uruchomić aplikację i pobrać strony z hostów: moskit.ie.tu.koszalin.pl, puma.tu.koszalin.pl, kos.man.koszalin.pl, termit.ie.tu.koszalin.pl; zmodyfikować aplikację i możliwość zapisu dokumentu do pliku. 7. Dla przykładu 6: uruchomić aplikację serwera i przetestować za pomocą przeglądarki IE oraz Netscape (jeżeli jest dostępny na stanowisku); uruchomić aplikację serwera i aplikację httpclient z przykładu 5 tak aby httpclient komunikował się z jhttpserver. 8. Dla przykładu 7: uruchomić aplikację serwera i przetestować za pomocą przeglądarki IE oraz Netscape (jeżeli jest dostępny na stanowisku); uruchomić aplikację serwera i aplikację httpclient z przykładu 5 tak aby httpclient komunikował się z jhttpserver. 9. Dla przykładu 8 i 9: zestawić połączenie między dwoma sąsiednimi stanowiskami; sprawdzić ile zgłoszeń klientów może oczekiwać w kolejce serwera; wyniki zanotować. - 26 -