Zmęczeni już tą biblioteką Swing? Spokojnie, zmieniamy temat na całkiem inny i przejdziemy teraz do zagadnień związanych z połączeniem sieciowym. Java pozwala na łatwe tworzenie połączenia sieciowego na podstawie architektury klient-serwer. Przedstawiam Wam klasę "Socket" - to od niej zaczyna się cała historia.

OBIEKT "SOCKET" POCZĄTKIEM DROGI DO POŁĄCZENIA

Tytułowy obiekt stanowi elementarny budulec każdego połączenia sieciowego. To jest najzwyklejsze w świecie gniazdo sieciowe. Tworząc jego instancję, zwykle podaje się dwa parametry: adres IP lub nazwę hosta do którego chcemy się podłączyć oraz numer portu TCP. Szczegóły znajdują się w osobnym wpisie, wystarczy kliknąć tutaj. Potem taki "Socket" podpina się pod strumień wejściowy (jeśli odczyt danych z gniazda) lub wyjściowy (jeśli zapis danych do gniazda). Należy też wspomnieć, że próby nawiązania połączenia mogą zakończyć się porażką więc konieczne jest otoczenie kodu obsługą wyjątków.

Aby zaprezentować działanie praktyczne, potrzeba i klienta, i serwera. W Javie, obu "aktorów" programuje się inaczej. Klient po prostu tworzy nowy "Socket" w bloku "try-catch" i łączy go ze strumieniem wejściowym zwanym "InputStreamReader", a serwer ma za zadanie przechwytywać klientów i ich gniazda w nieskończonej pętli "while". W środku niej z kolei, dorzuca się wszystko to, co ma stanowić czynności wykonywane po stronie serwera. Na przykładzie skorzystamy z "PrintWriter" celem wypisania komunikatu jako odpowiedzi w terminalu klienta.

Znam ten zawód na tyle dobrze, aby wiedzieć, że same słowa nie będą w stanie poruszyć dostatecznie zwojów mózgowych, aby to sobie wyobrazić. Wobec tego, zastosujemy dwa osobne programy współdzielące tę samą klasę "main" oraz skorzystamy z prywatnego adresu IP znanego pod hasłem "localhost", aby wszystko odbywało się za pomocą jednego stanowiska komputerowego. Zróbcie sobie dwa osobne projekty i wklejcie poniższe kody:

  • KLASA "Main"
public class Main
{
	public static void main(String[] args)
	{
		new Launcher();
	}
}
  • KLASA "Launcher" (klient)
import java.net.Socket;
import java. io.IOException;
import java. io.BufferedReader;
import java. io.InputStreamReader;

public class Launcher
{
	public Launcher()
	{
		try
		{
			initiateConnection();
		}
		catch (Exception exception)
		{
			exception.printStackTrace();
		}
	}

	private void initiateConnection() throws IOException
	{
		Socket socket = new Socket("127.0.0.1", 6700);
		InputStreamReader isr = new InputStreamReader(socket.getInputStream());
		BufferedReader br = new BufferedReader(isr);

		System.out.println(br.readLine());
		br.close();
	}
}
  • KLASA "Launcher" (serwer)
import java.net.Socket;
import java.net.ServerSocket;
import java. io.IOException;
import java. io.PrintWriter;

public class Launcher
{
	public Launcher()
	{
		try
		{
			handleConnections();
		}
		catch (Exception exception)
		{
			exception.printStackTrace();
		}
	}

	private void handleConnections() throws IOException
	{
		ServerSocket ss = new ServerSocket(6700);

		while (true)
		{
			Socket socket = ss.accept();
			PrintWriter pw = new PrintWriter(socket.getOutputStream());

			pw.println("Połączenie z serwerem zostało nawiązane.");
			pw.close();
		}
	}
}

Musicie dostrzec jedną bardzo ważną rzecz! Podane numery portów muszą być ze sobą zgodne (w naszym przypadku to jest 6700)!!! Ponadto, wysoce zalecanym jest, aby był on powyżej 1023. Dlaczego konkretnie, napisane jest tutaj. "Socket" jest po stronie klienta, a serwer dodatkowo musi być zaopatrzony w "ServerSocket", który będzie odpowiedzialny za "przyjmowanie" klientów i nawiązywanie z nimi połączenia. Aby zaobserwować prawidłowe działanie tej operacji, musicie najpierw skompilować i uruchomić program serwera, a potem NIE PRZERYWAJĄC DZIAŁANIA SERWERA, skompilować sobie program klienta i go uruchomić. Dopiero wtedy można liczyć na oczekiwane połączenie klienta z serwerem.

Traktujcie to jako bardzo gigantyczne uproszczenie operacji nawiązywania połączenia. Takie manewry wymagają wielu innych usprawnień jak na przykład działanie współbieżne. W chwili obecnej, nasz serwer jest w stanie "obsłużyć" tylko jeden "Socket" klienta w tym samym czasie. W praktyce, żaden inny klient nie może nawiązać połączenia dopóki obecny klient, który się podłączył, nie zakończy swojego połączenia. Gdyby takie działanie wypuścić na rynek, okazałoby się ono bezużyteczne, dlatego też wprowadza się do takich spraw osobne "wątki", które mają na celu egzekwowanie wielu czynności w tym samym czasie. Ten przypadek idealnie pasuje do zastosowania wątków. Artykuł pod tym linkiem poszerza temat wątków w Javie.

Odpowiedź od serwera za pomocą obiektów "Socket" w Javie

Zrzut ekranu ukazujący pomyślne nawiązanie połączenia z serwerem lokalnym poprzez obiekt "Socket" klienta, który otrzymał odpowiedź w formie komunikatu.


Na sam początek tyle informacji wystarczy. Pamiętajcie na odchodne, że każdy strumień należy zamknąć korzystając z metody "close". Bardzo łatwo o tym zapomnieć. Dziękuję za przeczytanie.

PODOBNE ARTYKUŁY