IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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 serveru
  • IServerThreadFactory - 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ákno
  • shutdown(), 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í ServerSocketu. 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 22x (127.44 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

Předchozí článek
Java server - Google Guice
Všechny články v sekci
Server pro klientské aplikace v Javě
Přeskočit článek
(nedoporučujeme)
Kvíz - Parametry serveru, vlákna a Google Guice
Článek pro vás napsal Petr Štechmüller
Avatar
Uživatelské hodnocení:
1 hlasů
Autor se věnuje primárně programování v Javě, ale nebojí se ani webových technologií.
Aktivity