Lab - Aplikacje TCP-IP

TCP (Transmission Control Protocol) to protokół komunikacyjny zorientowany na połączenie, który zapewnia niezawodne, uporządkowane i bezbłędne dostarczanie danych między dwoma hostami. Dzięki mechanizmom kontroli błędów i retransmisji TCP jest podstawą działania wielu aplikacji sieciowych, takich jak przeglądarki internetowe, poczta elektroniczna czy transfer plików.

W języku Java do tworzenia połączeń TCP/IP wykorzystywane są głównie klasy:

  • Socket – dla klienta,

  • ServerSocket – dla serwera.

Adresacja połączeń

Aby dostarczać bezbłędnie pakiety do odpowiedniego hosta wymagana jest odpowiednia adresacja. W tym celu w języku Java można wykorzystać klasę abstrakcyjną InetAddress, Która służy reprezentowania adresów IP i nazw hostów. Umożliwia m.in. wyszukiwanie adresów IP dla nazw domen, sprawdzanie dostępności hosta czy pobieranie informacji o lokalnym komputerze.

Do czego można wykorzystać InetAddress

  1. Odnajdywanie adresów IP hostów lub nazw hostów po adresie IP.

    InetAddress address = InetAddress.getByName("www.google.com");
    System.out.println("Adres IP: " + address.getHostAddress());

    lub

    InetAddress address = InetAddress.getByAddress(new byte[]{(byte)142, (byte)250, (byte)180, (byte)78});
    System.out.println("Nazwa Hosta: " + address.getHostName());
  2. Pobieranie informacji o lokalnym urządzeniu.

    InetAddress local = InetAddress.getLocalHost();
    System.out.println("Lokalny host: " + local.getHostName());
    System.out.println("Adres IP: " + local.getHostAddress());
  3. Sprawdzenie dostępności hosta (ping).

    InetAddress google = InetAddress.getByName("www.google.com");
    boolean reachable = google.isReachable(3000); // 3000 ms
    System.out.println("Czy host osiągalny? " + reachable);
  4. Obsługa adresów IPv4 i IPv6

    InetAddress[] addresses = InetAddress.getAllByName("example.com");
    for (InetAddress addr : addresses) {
        System.out.println(addr);
    }
  5. Integracja z klasami Socket i SocketServer

    InetAddress serverAddress = InetAddress.getByName("192.168.0.101");
    Socket socket = new Socket(serverAddress, 1234)

Tworzenie aplikacji serwerowej

Podstawową klasą wykorzystaną do stworzenia instancji serwera jest klasa ServerSocket, dzięki tej klasie możemy utworzyć nasłuchiwanie na połączenia przychodzące od klientów na określonym porcie i akceptować połączenia.

Przykład utworzenia instancji serwera

    import java.io.*;
    import java.net.*;
    
    public class SimpleServer {
        public static void main(String[] args) {
            int port = 1234; // numer portu
    
            try (ServerSocket serverSocket = new ServerSocket(port)) {
                System.out.println("Serwer nasłuchuje na porcie " + port);
    
                Socket clientSocket = serverSocket.accept(); // oczekiwanie na klienta
                System.out.println("Połączono z klientem: " + clientSocket.getInetAddress());
    
                BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
    
                String message = in.readLine();  // odbiór wiadomości od klienta
                System.out.println("Otrzymano: " + message);
    
                out.println("Witaj kliencie! Otrzymałem: " + message); // odpowiedź serwera
    
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

Tworzenia aplikacja klienta

W sytuacji kiedy wymagane jest połączenie z innym procesem lub innym urządzeniem pełniącym rolę serwera, należy skorzystać z klasy Socket, dzięki której możemy nawiązać połączenie z instancją serwera na określonym adresie i porcie.

Przykład instancji klienta

    import java.io.*;
    import java.net.*;
    
    public class SimpleClient {
        public static void main(String[] args) {
            String host = "localhost"; // adres serwera
            int port = 1234;           // port serwera
    
            try (Socket socket = new Socket(host, port)) {
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
    
                out.println("Cześć, tu klient!"); // wysłanie wiadomości
                String response = in.readLine();  // odbiór odpowiedzi
    
                System.out.println("Odpowiedź serwera: " + response);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

Powyższe dwa przykłady kodu przedstawiają aplikacje klienta i serwera działające tylko na jedno połączenie. Po zakończeniu połączenia aplikacje kończą pracę. Aplikacje wykonują nie szyfrowane połączenie, aby instancja serwera działała lepiej należy ją zmodyfikować dodając wielowątkowość, tak aby każde połączenie było uruchamiane w osobnym wątku. Takie rozwiązanie pozwala na większą liczbę połączeń oraz większą możliwość monitorowania instancji serwerowej.

Zadania

  1. Zaimplementuj prosty chat – klient wysyła wiadomość, a serwer ją odsyła w zmodyfikowanej formie (np. wielkimi literami).

  2. Rozszerz klienta o możliwość wysyłania kilku wiadomości do serwera. (wymagań wiedza z tematu 1)

  3. Rozszerz aplikację z zadania 2 o obsługę wielu połączeń jednocześnie, stwórz rozwiązanie, w którym każde połączenie będzie obsługiwane w osobnym wątku. Zaproponuj rozwiązanie za pomocą, którego będzie możliwość monitorowania ile połączeń jest aktywnych.

  4. Stwórz mechanizm do zarządzania połączeniami z zadania 3, w taki sposób aby można było sprawdzić ile jest połączonych użytkowników i wyświetlić informacje o nim typu nazwa hosta czy adres IP skorzystaj z metod getInetAddress().getHostAddress() czy getInetAddress().getHostName()

Last updated