Hlavní navigace

Síťování v Javě: První server

27. 4. 2006
Doba čtení: 4 minuty

Sdílet

V minulé části tohoto seriálu jsme si popsali několik tříd z balíku java.net a vytvořili jednoduchého telnet klienta. Dnes pro něj konečně najdeme využití - začneme totiž s programováním serverů. Stejně jako v prvním díle si nejdříve popíšeme důležité části java.net API. Potom budeme pokračovat dalším praktickým příkladem - vyrobíme si time server.

Třídy

Opět budeme používat již popsané třídy Socket, InetAddress a InetSocketAddress z balíku java.net. Dnes si k nim přidáme jednu další – ServerSocket.

java.net.Server­Socket

Objekt typu ServerSocket je srdcem každé serverové aplikace. Dokáže se připojit na síťové rozhraní a naslouchat tam, umí přijímat nově připojené klienty a poskytuje nám přístup k jejich vstupním a výstupním proudům.

Zde uvádím nejdůležitější metody třídy java.net.Server­Socket:

Metody třídy java.net.Server­Socket
void bind(SocketAddress endpoint) Serverový soket začne naslouchat na síťovém rozhraní a portu zadaném v parametru endpoint.
Socket accept() Vrátí nově přijatého klienta. Pozor, tato metoda pozastaví běh aktuálního vlákna, dokud se někdo nepřipojí!
void close() Uzavře soket. Ten pak přestane naslouchat novým spojením. Tuto metodu nikdy nezapomeňte zavolat před ukončením programu.

Kromě bind() můžeme použít jeden ze tří konstruktorů, které tuto metodu na základě uvedených parametrů samy volají. Postup, kdy soket začne naslouchat okamžitě při vytvoření, ovšem není ve většině situací příliš vhodný.

Nevidíte zde žádné funkce pro komunikaci s klientem? Ano, to je v pořádku. Jedniná možnost komunikace je totiž uložení odkazu na soket vrácený metodou accept() a pomocí Socket.getOutput­Stream() a Socket.getInput­Stream() získat datové proudy.

Shrnutí

Pojďme si shrnout, co víme o třídě ServerSocket. Vysvětlíme si, jakým způsobem se její objekty typicky používají.

Vytvoříme si novou instanci ServerSocket. Pokud jsme ji nesestavili konstruktorem, který automaticky spustí naslouchání, musíme zavolat metodu bind() a jako parametr jí předat adresu síťového rozhraní a port. Dále je užitečné použít accept(). Tato metoda zablokuje aktuální vlákno (tj. vlákno, ve kterém je volána), opět jej uvede do chodu, jakmile se připojí nějaký klient, a vrátí odkaz na jeho soket. Veškerý přenos dat probíhá prostřednictvím vstupního a výstupního proudu klientova soketu. Po obsloužení je dobré soket uzavřít metodou close(), čímž klienta odpojíme. Server deaktivujeme také zavoláním close().

Časový server

Nyní využijeme znalosti, které jsme získali ve dvou dosavadních dílech seriálu. Společně si sestavíme jednoduchý time server. Ukážeme si na něm, jak správně používat třídu ServerSocket a jeden postup, který nám velice usnadní práci. Zapisovat text jako proud bajtů není příliš pohodlné, využijeme proto třídu java.io.PrintStre­am, která nám umožní posílat soketem řetězce.

Požadavky na časový server

Byli bychom rádi, kdyby náš server splňoval tyto vlastnosti:

  • Aktuální čas bude klientovi odeslán ihned po připojení. Pak klienta odpojíme.
  • Aplikace nesmí zatěžovat systém – nebude tudíž vícevláknová.
  • Co nejkratší kód :-)

Tyto podmínky jsem vybral zcela záměrně ;-) Jejich splnění nám totiž nebude činit vůbec žádné potíže.

Algoritmus programu

Přímo se nabízí tento celkem jednoduchý postup:

  1. Vytvoříme nový ServerSocket.
  2. Zavoláním metody bind() aktivujeme naslouchání.
  3. Metodou accept() přijmeme klienta.
  4. Do výstupního proudu soketu vráceného touto metodou zapíšeme aktuální čas.
  5. Soket uzavřeme – odpojíme klienta.
  6. Vrátíme se na bod č. 3.

To je vše. Zde je zdrojový kód:

CS24 tip temata

Zdrojový kód

import java.io.*;
import java.net.*;

/** Hlavní třída celého serveru. */
public class TimeServer {

    /** Port, na kterém budeme naslouchat. */
    public static final int PORT = 10997;


    /** Hlavní metoda programu. */
    public static void main(String[] args) {

        ServerSocket server = null; //serverový soket

        try {
            server = new ServerSocket(); //vytvořit serverový soket
            //získat adresu pro všechna síťová rozhraní
            SocketAddress addr = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), PORT);

            server.bind(addr); //zapnout naslouchání


            while(true) {
                Socket client = server.accept(); //počkat na přijetí klienta
                //vytvořit proud, do kterého je možné posílat řetězce
                PrintStream output = new PrintStream(client.getOutputStream());

                output.println(new java.util.Date()); //odeslat aktuální čas a datum

                try {
                    output.close(); //zavřít náš vlastní výstupní proud
                    client.close(); //odpojit klienta
                }
                catch(IOException e) {
                    e.printStackTrace();
                }
            }
        }
        catch(IOException e) {
            e.printStackTrace();
        }
        finally {
            //v každém případě uzavřeme serverový soket
            if(server != null) {
                try {
                    server.close();
                } catch(IOException e) {}
            }
        }
    }

} 

Poznámky k programu

Prostudujte si kód, klidně ho přeložte (javac TimeServer.java) a aplikaci otestujte (java TimeServer). Pak si povšimněte následujících věcí:

  • Ihned po volání accept() vytváříme výstupní proud typu java.io.PrintStre­am. Je to onen zmiňovaný postup, který umožní posílat místo obsahu bytových polí řetězce. Možná o tom nevíte, ale třídu PrintStream určitě důvěrně znáte. V příkazu System.out.prin­tln(„Hello world“) ze známého „programu“ :-) je totiž proměnná out právě objekt této třídy. Vyplývá z toho, že do proudu posíláme řetězce stejně jako kdybychom tiskli na konzoli.
  • Ukončení serveru je uvnitř bloku finally. Tím máme zaručeno řádné uzavření soketu i v případě jakékoliv IO výjimky. Tato část kódu sice nemá u naší aplikace téměř žádný význam, avšak při sestavování velkých serverů se vám může její absence opravdu škaredě vymstít.

Závěr

Tak jsme se dostali zase o krůček dále. Popsali jsme si novou část java.net API a okamžitě jsme ji využili při programování primitivního time serveru. V příštím díle nás čeká teorie opravdu minimum, připravte se však na první pořádný program. Prozradím vám jen, že cílem našeho snažení bude chatovací server.

Autor článku