4. diel - Java server - Vlákno servera
V minulej lekcii, Java server - Google Guice , sme vložili správu závislostí do rúk knižnice Google Guice. Dnes si vytvoríme základnú kostru vlákna nášho Java servera.
Funkcia vlákna
Toto vlákno bude slúžiť ako hlavný prístupový bod. Tu sa bude nadväzovať spojenie s klientmi.
Vytvoríme si nový balík core
, do ktorého budeme vkladať
všetku dôležitú funkčnosť servera.
Továreň vlákna
Začneme podobne, ako v minulej lekcii. Vytvoríme továreň, ktorá nám
vytvorí inštanciu vlákna. V balíčku core
vytvoríme nový
balík server
, v ktorom vytvoríme nasledujúce triedy a
rozhrania:
IServerThread
- rozhranie poskytujúce metódy pre komunikáciu s vláknom serveraIServerThreadFactory
- rozhranie obsahujúce metódu pre vytvorenie inštancie rozhraniaIServerThread
ServerThread
- implementácia rozhraniaIServerThread
ServerThreadFactory
- implementácia rozhraniaIServerThreadFactory
Iba toto rozhranie vložíme priamo do balíčka core
:
IThreadControl
- pomocné rozhranie pre prácu s vláknami
Najskôr zadefinujeme metódy pre rozhranie IThreadControl
. Toto
rozhranie bude obsahovať dve metódy:
start()
, ktorá spustí vláknoshutdown()
, pomocou ktorej budeme vlákno informovať, že má začať ukončovacie sekvenciu
Signatúra metód bude nasledujúce:
void start(); void shutdown();
Rozhranie IServerThread
necháme dediť z rozhrania
IThreadControl
.
Rozhranie IServerThreadFactory
bude obsahovať jednu
továrenské metódu pre výrobu inštancie triedy
IServerThread
:
IServerThread getServerThread(IParameterProvider parameters) throws IOException;
Implementácia rozhrania
Rozhranie sme si nadefinovali, tak je poďme implementovať. Začneme triedou
ServerThread
, ktorá implementuje rozhranie
IServerThread
. Triedu ešte upravíme tak, že ju necháme dediť
od triedy Thread
. Definícia triedy bude teda vyzerať takto:
class ServerThread extends Thread implements IServerThread
V triede si zadefinujeme jednu triedny konštantu:
private static final int SOCKET_TIMEOUT = 5000;
ďalej jednu inštančné konštantu:
private final int port;
a jednu inštančné premennú:
private boolean running = false;
ktorá bude indikovať, či ak má vlákno bežať, alebo sa ukončiť.
Ďalej vytvoríme konštruktor, ktorý prijíma jediný parameter:
int port
. V konstruktoru nastavíme názov vlákna na
"ServerThread"
a inicializujeme inštančný konštantu
port
:
ServerThread(int port) throws IOException { super("ServerThread"); this.port = port; }
Nakoniec implementujeme metódy, ktoré nám definuje rozhranie, teda:
@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 metóde shutdown()
nastavíme premennej running
hodnotu false
a zavoláme join()
, aby sme počkali na
ukončenie vlákna. Metóda run()
vytvorí novú inštanciu triedy
ServerSocket
a nastaví timeout na hodnotu konštanty
SOCKET_TIMEOUT
, teda 5 sekúnd. Tým zabezpečíme, že sa
každých 5 sekúnd vyvolá výnimka SocketTimeoutException
a
skontroluje sa premenná running
. Nasleduje volanie metódy
accept()
nad inštancií ServerSocket
u. Toto volanie
je blokujúce, čo znamená, že vlákno nebude vykonávať
žiadnu činnosť, kým sa nepripojí klient, alebo sa nevyvolá výnimka.
Spracovanie novo existujúcom spojení necháme na ďalšiu lekciu. Tým by sme
boli pre túto chvíľu s triedou ServerThread
hotoví a môžeme
sa pustiť do implementácie továrne.
Implementácia továrne
Triede ServerThreadFactory
pridáme anotáciu
@Singleton
z knižnice Google Guice. Táto anotácie nám zaistí,
že kedykoľvek v budúcnosti budeme žiadať o továreň, dostaneme jednu a tú
istú inštanciu. Trieda ServerThreadFactory
musí implementovať
jedinú metódu getServerThread()
. Metóda prijíma ako parameter
rozhrania IParameterProvider
, ktoré poskytuje parametre z
príkazového riadku. V továrni si zadefinujeme predvolené hodnoty parametrov,
ktoré sa uplatňujú v prípade, že by sme nejaký parameter neodovzdali pri
spustení servera:
// 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;
Teraz môžeme vyplniť telo metódy 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 metóde získame jednotlivé parametre a nakoniec vytvoríme a vrátime
novú inštanciu triedy ServerThread
. V budúcnosti využijeme aj
zvyšné parametre.
Po implementácii všetkých rozhraní konkrétnymi triedami môžeme
zaregistrovať továreň na vlákno serveri v triede ServerModule
.
Registrácia bude rovnaká ako v prípade továrne na parametre:
bind(IServerThreadFactory.class).to(ServerThreadFactory.class);
Nakoniec sa presunieme do triedy Server
, kde všetko
zadrátujeme dohromady. Najskôr pridáme ďalšie inštančný konštantu typu
IServerThreadFactory
:
private final IServerThreadFactory serverThreadFactory;
a tiež pridáme rovnaký parameter do konstruktoru, kde továreň inicializujeme:
@Inject public Server(IParameterFactory parameterFactory, IServerThreadFactory serverThreadFactory) { this.parameterFactory = parameterFactory; this.serverThreadFactory = serverThreadFactory; }
Teraz upravíme metódu 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 metóde najskôr získame parametre ktoré odovzdáme továrni na vlákno
serveri, ktorá nám vráti inštanciu triedy IServerThread
.
Metódou start()
spustíme vlákno servera. Vlákno sa spustí, ale
pretože sme nenadefinovali žiadnu činnosť, okamžite sa ukončí. Nasleduje
nekonečná slučka, ktorá očakáva vstup od užívateľa, kým používateľ
nezadá slovo "exit"
. Keď používateľ zadá "exit"
,
začne sa server ukončovať. Metódou shutdown()
informuje vlákno
serveru, že má začať ukončovať svoj beh.
To by bolo z dnešnej lekcie všetko. Nabudúce, v lekcii Riešené úlohy k 1.-4. lekciu Server v Jave , vytvoríme triedu, ktorá bude spracovávať prichádzajúce klientov.
V nasledujúcom cvičení, Riešené úlohy k 1.-4. lekciu Server v Jave, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 24x (127.44 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java