IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

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 servera
  • IServerThreadFactory - rozhranie obsahujúce metódu pre vytvorenie inštancie rozhrania IServerThread
  • ServerThread - implementácia rozhrania IServerThread
  • ServerThreadFactory - implementácia rozhrania IServerThreadFactory

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ákno
  • shutdown(), 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

 

Predchádzajúci článok
Java server - Google Guice
Všetky články v sekcii
Server pre klientskej aplikácie v Jave
Preskočiť článok
(neodporúčame)
Riešené úlohy k 1.-4. lekciu Server v Jave
Článok pre vás napísal Petr Štechmüller
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje primárně programování v Javě, ale nebojí se ani webových technologií.
Aktivity