Strumienie, gniazda sieciowe i wątki - komunikator internetowy (zestaw I)

 

 

Ważnym obszarem zastosowań platformy Java jest programowanie aplikacji sieciowych. Java Platform API oferuje doskonałe wsparcie dla tego rodzaju zastosowań, w postaci klas bibliotecznych, pozwalających na wykorzystanie obiektów reprezentujących tego rodzaju pojęcia jak gniazda sieciowe, strumienie danych, czy wątki. Podstawowy system obsługi wejścia-wyjścia w Javie (pakiet java.io) jest oparty o cztery abstrakcyjne klasy biblioteczne InputStream, OutputStream, Reader i Writer (oraz odpowiednie klasy konkretne, np. BufferedReader, PrintWriter). InputStreami OutputStream reprezentują strumienie binarne, natomiast Reader i Writer reprezentują strumienie tekstowe. Nasza implementacja tekstowego komunikatora internetowego będzie wykorzystywała strumienie tekstowe.

 

 

ćw. 1

Poniżej znajdziesz kod źródłowy klas Serwer i Klient. Zestaw z nich dwie oddzielne jednostki kompilacji, skompiluj każdą z nich, uruchom (najpierw serwer, potem klienta) i przetestuj. Oczywiście ćwiczenie to możesz przeprowadzić także z kolegą/koleżanką używając dwóch komputerów (z dostępem do sieci).

 

Zarówno klient jak i serwer posiadają statyczne pole PORT przechowujące numer portu (int z  przedziału 0 do 65535 - numery portów od 0 do 1023 są określane jako "well known ports" i zarezerwowane na standardowe usługi takie, jak www, ssh czy poczta elektroniczna, numery od 1024 do 49151 są określane jako "registered", a numery od 49152 do 65535 jako "dynamic/private"). Klasa Klient posiada także pole statyczne HOST przechowujące adres serwera z którym klient nawiąże połączenie.

 


import java.io.*;
import java.net.*;

public class Serwer
{
   public static final int PORT=50007;
   
   public static void main(String args[]) throws IOException                  
   {                                                                         
      //tworzenie gniazda serwerowego                                        
      ServerSocket serv;                                                     
      serv=new ServerSocket(PORT);                                           
                                                                             
      //oczekiwanie na polaczenie i tworzenie gniazda sieciowego             
      System.out.println("Nasluchuje: "+serv);                               
      Socket sock;                                                           
      sock=serv.accept();                                                    
      System.out.println("Jest polaczenie: "+sock);                          
                                                                             
      //tworzenie strumienia danych pobieranych z gniazda sieciowego         
      BufferedReader inp;                                                    
      inp=new BufferedReader(new InputStreamReader(sock.getInputStream()));  
                                                                             
      //komunikacja - czytanie danych ze strumienia                          
      String str;                                                            
      str=inp.readLine();                                                    
      System.out.println("<Nadeszlo:> " + str);                              
                                                                             
      //zamykanie polaczenia                                                 
      inp.close();                                                           
      sock.close();                                                          
      serv.close();                                                          
   }                                                                         
}

import java.io.*;
import java.net.*;

public class Klient
{
   public static final int PORT=50007;
   public static final String HOST = "127.0.0.1";
   
   public static void main(String[] args) throws IOException                            
   {                                                                                   
      //nawiazanie polaczenia z serwerem                                               
      Socket sock;                                                                     
      sock=new Socket(HOST,PORT);                                                      
      System.out.println("Nawiazalem polaczenie: "+sock);                              
                                                                                       
      //tworzenie strumieni danych pobieranych z klawiatury i dostarczanych do socketu 
      BufferedReader klaw;                                                             
      klaw=new BufferedReader(new InputStreamReader(System.in));                       
      PrintWriter outp;                                                                
      outp=new PrintWriter(sock.getOutputStream());                                    
                                                                                       
      //komunikacja - czytanie danych z klawiatury i przekazywanie ich do strumienia   
      System.out.print("<Wysylamy:> ");                                                
      String str=klaw.readLine();                                                      
      outp.println(str);                                                               
      outp.flush();                                                                    
                                                                                       
      //zamykanie polaczenia                                                           
      klaw.close();                                                                    
      outp.close();                                                                    
      sock.close();                                                                    
   }                                                                                   
}

 

ćw. 2

Rozwiń powyższy przykład w taki sposób żeby po przesłaniu jednego komunikatu nie kończył działania, ale pozwalał klientowi na przesyłanie kolejnych komunikatów do serwera.

 

ćw. 3

Na podstawie powyższego przykładu i informacji z wykładu napisz prostą implementację tekstowego komunikatora internetowego działającego w trybie simpleks. Twoja implementacja powinna obejmować następujące etapy - (1) nawiązanie połączenia (ten etap został już zrobiony w przykładzie), (2) utworzenie obiektów reprezentujących strumienie danych pobieranych z klawiatury i z gniazda sieciowego oraz strumienie danych dostarczanych do gniazda sieciowego, (3) etap właściwej komunikacji w trybie simpleks (pętla wykonująca naprzemian odbieranie i wysyłanie komunikatów tekstowych) oraz (4) zakończenie komunikacji i (5) zamknięcie połączenia. Komunikacja powinna się odbywać do momentu przesłania przez którąkolwiek ze stron komunikatu "koniec" albo "KONIEC" (pomocna może tu być metoda equalsIgnoreCase() klasy String). Po przesłaniu takiego komunikatu zarówno klient jak i serwer powinny wypisać komunikat "Koniec polaczenia" i zakończyć działanie.

 

ćw. 4

Po stronie klienta obsłuż wyjątek java.net.UnknownHostException (try{}catch()) który wystąpi np. wtedy kiedy klient próbuje nawiązać połączenie z serwerem w sytuacji kiedy pod wskazanym adresem i numerem portu nie działa żaden serwer. W przypadku wystąpienia tego wyjątku Twoj komunikator powinien wypisać komunikat "Polaczenie zostalo przerwane" i zakończyć działanie.

 

ćw. 5

Po stronie klienta obsłuż wyjątek java.net.SocketException (try/catch) oznaczający wystąpienie błędu związanego z protokołem TCP. Wystąpienie tego wyjątku można zasymulować wyciągając wtyczkę z gniazda karty sieciowej.