Lekce 4 - Java server - Vlákno serveru
V minulé lekci, Java server - Google Guice, jsme vložili správu závislostí do rukou knihovny Google guice.
Dnes si vytvoříme základní kostru vlákna našeho Java serveru.
Funkce vlákna
Toto vlákno bude sloužit jako hlavní přístupový bod. Zde se bude navazovat spojení s klienty.
Vytvoříme si nový balíček core
, do kterého budeme vkládat
veškerou důležitou funkčnost serveru.
Továrna vlákna
Začneme podobně, jako v minulé lekci. Vytvoříme továrnu, která nám
vytvoří instanci vlákna. V balíčku core
vytvoříme nový
balíček server
, ve kterém vytvoříme následující třídy a
rozhraní:
IServerThread
- rozhraní poskytující metody pro komunikaci s vláknem serveruIServerThreadFactory
- rozhraní obsahující metodu pro vytvoření instance rozhraníIServerThread
ServerThread
- implementace rozhraníIServerThread
ServerThreadFactory
- implementace rozhraníIServerThreadFactory
Pouze toto rozhraní vložíme přímo do balíčku core
:
IThreadControl
- pomocné rozhraní pro práci s vlákny
Nejdříve zadefinujeme metody pro rozhraní IThreadControl
.
Toto rozhraní bude obsahovat dvě metody:
start()
, která spustí vláknoshutdown()
, pomocí které budeme vlákno informovat, že má zahájit ukončovací sekvenci
Signatura metod bude následující:
void start(); void shutdown();
Rozhraní IServerThread
necháme dědit z rozhraní
IThreadControl
.
Rozhraní IServerThreadFactory
bude obsahovat jednu tovární
metodu pro výrobu instance třídy IServerThread
:
IServerThread getServerThread(IParameterProvider parameters) throws IOException;
Implementace rozhraní
Rozhraní jsme si nadefinovali, tak je pojďme implementovat. Začneme
třídou ServerThread
, která implementuje rozhraní
IServerThread
. Třídu ještě upravíme tak, že ji necháme
dědit od třídy Thread
. Definice třídy bude tedy vypadat
takto:
class ServerThread extends Thread implements IServerThread
Ve třídě si nadefinujeme jednu třídní konstantu:
private static final int SOCKET_TIMEOUT = 5000;
dále jednu instanční konstantu:
private final int port;
a jednu instanční proměnnou:
private boolean running = false;
která bude indikovat, zda-li má vlákno běžet, nebo se ukončit.
Dále vytvoříme konstruktor, který přijímá jediný parametr:
int port
. V konstruktoru nastavíme název vlákna na
"ServerThread"
a inicializujeme instanční konstantu
port
:
ServerThread(int port) throws IOException { super("ServerThread"); this.port = port; }
Nakonec implementujeme metody, které nám definuje rozhraní, tedy:
@Override public void shutdown() { running = false; try { join(); } catch (InterruptedException ignored) {} } @Override public void run() { try (ServerSocket serverSocket = new ServerSocket(port)) { serverSocket.setSoTimeout(SOCKET_TIMEOUT); while (running) { try { final Socket socket = serverSocket.accept(); } catch (SocketTimeoutException ignored) {} } } catch (IOException e) { e.printStackTrace(); } }
V metodě shutdown()
nastavíme proměnné running
hodnotu false
a zavoláme join()
, abychom počkali na
ukončení vlákna. Metoda run()
vytvoří novou instanci třídy
ServerSocket
a nastaví timeout na hodnotu konstanty
SOCKET_TIMEOUT
, tedy 5 vteřin. Tím zajistíme, že se každých 5
vteřin vyvolá vyjímka SocketTimeoutException
a zkontroluje se
proměnná running
. Následuje volání metody
accept()
nad instancí ServerSocket
u. Toto volání je
blokující, což znamená, že vlákno nebude vykonávat
žádnou činnost, dokud se nepřipojí klient, nebo se nevyvolá výjimka.
Zpracování nově navázaného spojení necháme na další lekci. Tím bychom
byli pro tuto chvíli s třídou ServerThread
hotoví a můžeme se
pustit do implementace továrny.
Implementace továrny
Třídě ServerThreadFactory
přidáme anotaci
@Singleton
z knihovny Google guice. Tato anotace nám zajistí, že
kdykoliv v budoucnu budeme žádat o továrnu, dostaneme jednu a tu samou
instanci. Třída ServerThreadFactory
musí implementovat jedinou
metodu getServerThread()
. Metoda přijímá jako parametr rozhraní
IParameterProvider
, které poskytuje parametry z příkazové
řádky. V továrně si zadefinujeme výchozí hodnoty parametrů, které se
použijí v případě, že bychom nějaký parametr nepředali při spuštění
serveru:
// Výchozí hodnota portu private static final int DEFAULT_SERVER_PORT = 15378; // Výchozí maximální počet klientů private static final int DEFAULT_MAX_CLIENTS = 3; // Výchozí velikost čekací fronty private static final int DEFAULT_WAITING_QUEUE_SIZE = 1;
Nyní můžeme vyplnit tělo metody getServerThread()
:
@Override public IServerThread getServerThread(IParameterProvider parameters) throws IOException { final int port = parameters.getInteger(CmdParser.PORT, DEFAULT_SERVER_PORT); final int maxClients = parameters.getInteger(CmdParser.CLIENTS, DEFAULT_MAX_CLIENTS); final int waitingQueueSize = parameters.getInteger(CmdParser.MAX_WAITING_QUEUE, DEFAULT_WAITING_QUEUE_SIZE); return new ServerThread(port); }
V metodě získáme jednotlivé parametry a nakonec vytvoříme a vrátíme
novou instanci třídy ServerThread
. V budoucnu využijeme i zbylé
parametry.
Po implementaci všech rozhraní konkrétními třídami můžeme
zaregistrovat továrnu na vlákno serveru ve třídě ServerModule
.
Registrace bude stejná jako v případě továrny na parametry:
bind(IServerThreadFactory.class).to(ServerThreadFactory.class);
Nakonec se přesuneme do třídy Server
, kde vše zadrátujeme
dohromady. Nejdříve přidáme další instanční konstantu typu
IServerThreadFactory
:
private final IServerThreadFactory serverThreadFactory;
a také přidáme stejný parametr do konstruktoru, kde továrnu inicializujeme:
@Inject public Server(IParameterFactory parameterFactory, IServerThreadFactory serverThreadFactory) { this.parameterFactory = parameterFactory; this.serverThreadFactory = serverThreadFactory; }
Nyní upravíme metodu run()
:
private void run(String[]args) throws IOException { final IParameterProvider parameters = parameterFactory.getParameters(args); final IServerThread serverThread = serverThreadFactory.getServerThread(parameters); serverThread.start(); while(true) { final String input = scanner.nextLine(); if ("exit".equals(input)) { break; } } serverThread.shutdown(); }
V metodě nejdříve získáme parametry které předáme továrně na
vlákno serveru, která nám vrátí instanci třídy
IServerThread
. Metodou start()
spustíme vlákno
serveru. Vlákno se spustí, ale protože jsme nenadefinovali žádnou činnost,
okamžitě se ukončí. Následuje nekonečná smyčka, která očekává vstup
od uživatele, dokud uživatel nezadá slovo "exit"
. Když
uživatel zadá "exit"
, začne se server ukončovat. Metodou
shutdown()
informuje vlákno serveru, že má začít ukončovat
svůj běh.
To by bylo z dnešní lekce vše.
V následujícím kvízu, Kvíz - Parametry serveru, vlákna a Google Guice, si vyzkoušíme nabyté zkušenosti z předchozích lekcí.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 25x (127.44 kB)
Aplikace je včetně zdrojových kódů v jazyce Java