C a C++ týden ITnetwork Flashka zdarma
Akce! Pouze tento týden sleva až 80 % na kurzy C++. Lze kombinovat s akcí 50 % bodů navíc na prémiový obsah!
Brno? Vypsali jsme pro vás nové termíny školení Základů programování a OOP v Brně!

Lekce 4 - Java server - Vlákno serveru

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

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. Příště, v lekci Java server - Správce spojení, vytvoříme třídu, která bude zpracovávat příchozí klienty.


 

Stáhnout

Staženo 12x (117.35 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

 

Článek pro vás napsal Petr Štechmüller
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
Autor se věnuje primárně programování v Jave, ale nebojí se ani webových technologií.
Předchozí článek
Java server - Google Guice
Všechny články v sekci
Server pro klientské aplikace v Javě
Miniatura
Následující článek
Java server - Správce spojení
Aktivity (2)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!