Hlavní navigace

Síťování v Javě: Úvod

Martin Majer

První díl seriálu o práci se sítí v Javě. Začneme zlehka teoretickým popisem několika důležitých tříd. Na závěr si naprogramujeme první aplikaci, kterou bude vlastní telnet klient.

V prvním díle tohoto seriálu si povíme, jaké balíky Javy slouží k práci se sítí. Podrobněji se zaměříme na třídy java.net.InetAd­dress, java.net.Inet­SocketAddress a java.net.Socket. Nakonec si ukážeme, jak vytvořit jednoduchého konzolového telnet klienta. Server vytvoříme v následujících článcích, dále budeme programovat vícevláknové aplikace pro více uživatelů. Ukážeme si, jak používat moderní New I/O (NIO) rozhraní Javy a skončíme u IRC chatovacího serveru.

Nepočítejte s žádným vyčerpávajícím výkladem teorie o Java API a implementaci TCP/IP a UDP protkolů. Pokud se budete chtít o dané třídě či rozhraní dozvědět více, nahlédněte do dokumentace k jejímu API. Seriál bude zaměřen výhradně na praktické příklady; teorii nutnou k jejich pochopení ale samozřejmě vypustit nelze.

Balíky

Pracovat budeme s balíky java.net, java.io a java.nio, tak si je nyní ve stručnosti popíšeme. První dva uvedené jsou součástí Java API už od své první verze. Balík java.net nabízí základní funkčnost pro jednodušší síťové aplikace. Říkám jednodušší, protože pro každý nově vytvořený soket je nutné spustit vlákno navíc. V java.io jsou sice umístěny třídy spíše pro práci se soubory, některé z nich jsou však nesmírně užitečné. A to nejen při síťování.

Mladší java.nio umožňuje práci s novým NIO rozhraní, které bylo začleněno ve verzi 1.4. Jeho hlavní předností oproti java.net je schopnost používat pouze jedno vlákno pro neomezené množství soketů (respektive klientů připojených k serveru), čímž se výrazně zvyšuje výkon aplikací. Za ostatní novinky z java.nio uvedu např. zcela nový přístup k souborům nebo možnost soubory uzamknout.

Třídy

java.net.InetAd­dress

Tato třída reprezentuje adresu v síti podle IP protokolu. Zde je vypsáno několik jejích metod:

Důležité metody z java.net.Ine­tAddress
static InetAddress getByAddress(byte[] addr) Vrací adresu na základě pole bajtů. 4 bajty pro IPv4, 16 bajtů pro IPv6.
static InetAddress getByName(String host) Vrací adresu podle hostname.
static InetAddress getLocalHost() Vrací adresu pro localhost.
boolean isReachable(int timeout) Zjistí, jestli se lze připojit k cíli na zadané adrese.

java.net.Inet­SocketAddress

InetSocketAddress je kombinace třídy InetAddress a portu. Tuto třídu budeme využívat k připojování soketů na server.

K vytváření nových instancí budeme nejčastěji volat konstruktory InetSocketAddress(InetAddress addr, int port) nebo InetSocketAddress(String hostname, int port).

java.net.Socket

Sockety jsou jedny z nejdůležitějších částí síťových aplikací, jejich prostřednictvím totiž probíhá veškerá síťová komunikace. V Javě se pro vytvářen soketů používá třída java.net.Socket. Zde uvádím, podobně jako u InetAddress, její nejdůležitější metody:

Důležité metody z java.net.Socket
void connect(Socke­tAddress endpoint) Připojí se k serveru. Při neúspěchu vyhodí výjimku  SocketTimeoutException.
InputStream getInputStream() Vrací vstupní proud soketu, ze kterého čteme příchozí data.
OutputStream getOutputStream() Vrací výstupní proud soketu, do kterého data zapisujeme.
void close() Uzavře soket.

Dokumentace

Více se o těchto třech třídách můžete dozvědět v dokumentaci k balíku java.net na adrese http://java.sun­.com/j2se/1.5­.0/docs/api/ja­va/net/package-summary.html.

Telnet klient

Po únavné teorii ;-) se konečně dostáváme k něčemu mnohem zajímavějšímu – ke slibovanému telnet klientovi. Klient bude běžet ve dvou vláknech. První vlákno bude vyčkávat na vstup z konzole a posílat data soketem na server, druhé bude dělat přesný opak. Čekat na data ze serveru a tisknout.

Vlákno TelnetThread

Všimněte si, že obě vlákna vlastně dělají stejnou věc: čtou z nějakého vstupního proudu a následně zapisují do proudu výstupního. To nám velice ulehčí práci, pro obě vlákna nám totiž stačí pouze jedna společná třída.

Nejdůležitější kód třídy TelnetThread se nachází v metodě run(). Do bufferu o velikosti 64 bytů načítá ze vstupního proudu data, která okamžitě a bez jakékoliv změny pošle na výstup. Ve chvíli, kdy se vstupní proud uzavře, vlákno se samo ukončí.

while(true) {
    int nbytes = is.read(b);
    if(nbytes == -1) break;
    os.write(b, 0, nbytes);
} 

Proměnná is odkazuje na vstup, proměnná os na výstup. Bytový buffer jsme pojmenovali jako b. Také si povšimněte podmínky, ve které zjišťujeme, jestli se počet přečtených bytů nbytes rovná hodnotě –1. Jakmile jakýkoliv vstupní proud vrátí tuto hodnotu, znamená to, že byl ukončen. U vstupního proudu soketu je tato návratová hodnota signálem odpojení.

Připojení soketu k serveru

Podle parametrů z konzole sestavíme adresu typu InetSocketAddress. Dále vytvoříme soket.

InetSocketAddress addr = new InetSocketAddress(hostname, port);
Socket socket = new Socket(); 

Nyní se pokusíme připojit. V případě neúspěchu vyvolá příkaz některou z dceřiných výjimek java.io.IOExcep­tion.

socket.connect(addr); 

Spuštění vláken TelnetThread

Vytvoříme obě dvě vlákna typu TelnetThread a jako parametry jim předáme použité proudy. Vlákno čtoucí z konzole nakonfigurujeme jako démona – nikdy totiž neskončí (vstupní proud konzole se neuzavře). To by ale bránilo ukončení aplikace po odpojení soketu. Na smrt démonových vláken však konec běhu aplikace nečeká, proto ho tak musíme označit. Obě vlákna spustíme, přičemž hlavní vlákno necháme čekat, dokud neskončí TelnetThread, které čte ze soketu.

Thread reading = new TelnetThread(socket.getInputStream(), System.out);
Thread writing = new TelnetThread(System.in, socket.getOutputStream());
writing.setDaemon(true);

reading.start();
writing.start();

reading.join(); 

Nakonec pro jistotu zavoláme metodu close() soketu.

Celý zdrojový kód

Zde je kompletní a detailně okomentovaný zdrojový kód telnetu:

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

/** Hlavní třída programu Telnet. Spouští se příkazem "java Telnet host port". */
public class Telnet {

    /** Hlavní metoda. Připojí se k serveru a spustí dvě vlákna TelnetThread. */
    public static void main(String[] args) {
        //ověřit počet parametrů
        if(args.length < 2) {
            System.out.println("Použití: java Telnet host port");
            return;
        }

        String hostname = args[0]; //získat hostname

        //získat port
        int port = 0;
        try {
            port = Integer.parseInt(args[1]); //převést parametr na číslo
        }
        catch(NumberFormatException e) {
            System.out.println("Neplatný port");
            System.exit(-1);
        }

        //vytvořit adresu a soket
        InetSocketAddress addr = new InetSocketAddress(hostname, port);
        Socket socket = new Socket();

        try {
            socket.connect(addr); //pokusit se připojit

            //vlákno, které čte ze soketu a tiskne na konzoli
            Thread reading = new TelnetThread(socket.getInputStream(), System.out);
            //vlákno, které čte z konzole a posílá data do soketu
            Thread writing = new TelnetThread(System.in, socket.getOutputStream());
            writing.setDaemon(true); //vytvořit démona

            //spustit vlákna
            reading.start();
            writing.start();

            reading.join(); //počkat odpojení
            socket.close(); //uzavřit soket
        }

        /* Probuzení hlavního vlákna z čekání. Nemělo by nastat. */
        catch(InterruptedException e) {
            e.printStackTrace();
        }
        /* Vypršel čas připojení k serveru. */
        catch(SocketTimeoutException e) {
            System.out.println("Nelze se připojit k serveru.");
            System.exit(-1);
        }
        /* Neznámý host. */
        catch(UnknownHostException e) {
            System.out.println("Neznámý host.");
            System.exit(-1);
        }
        /* Jiná IO výjimka. Obvykle NoRouteToHostException nebo ConnectException. */
        catch(IOException e) {
            System.out.println("IO chyba:");
            e.printStackTrace();
            System.exit(-1);
        }

    }


    /** Toto vlákno čeká na data ze zadaného vstupního proudu a okamžitě je přeposílá do výstupního proudu. */
    static class TelnetThread extends Thread {

        /** Vstupní proud. */
        private InputStream is;
        /** Výstupní proud. */
        private OutputStream os;

        /** Vytvoří nové vlákno pracující se zadanými proudy. */
        public TelnetThread(InputStream is, OutputStream os) {
            this.is = is;
            this.os = os;
        }

        /** Hlavní metoda vlákna. */
        public void run() {
            byte[] b = new byte[64]; //vytvořit buffer

            try {
                while(true) {
                    int nbytes = is.read(b); //přečíst bajty
                    if(nbytes == -1) break; //ověřit konec proudu
                    os.write(b, 0, nbytes); //zapsat bajty
                }
                System.out.println("Vstupní proud uzavřen.");
            }
            catch(IOException e) {
                System.out.println("IO chyba:");
                e.printStackTrace();
                System.exit(-1);
            }
        }

    }

} 

Spuštění telnetu

Telnet spustíte pomocí příkazu java Telnet host port:

wanto@karmaj:~/root/java_site> java Telnet www.google.com 80
GET / HTTP/1.1
Host: www.google.com

HTTP/1.1 200 OK
... 

Závěr

Dnes jsme si vysvětlili úplné základy programování síťových aplikací v Javě. Popsali jsme si tři důležité třídy a naprogramovali naší první klientskou aplikaci – telnet! Příště si ukážeme, jak vytvořit jednoduchý server – to abychom mohli náš telnet nějak využít :-).

STB2

Velká velikonoční soutěž o set-top-boxy

Zajímá vás digitální vysílání? Chcete sledovat televizní programy v digitální kvalitě? Pak se zúčastněte Velké velikonoční soutěže společnosti Internet Info, vydavatele serveru DigiZone.cz, a vyhrajte jeden z deseti set-top-boxů. Kvůli velikonoční výslužce už není nutné mlátit holky! Stačí správně odpovědět na soutěžní otázky a počkat na slosování výherců. Pokud nevyhrajete, nezoufejte. DigiZone.cz spustil internetový obchod se set-top-boxy, kde si určitě vyberete ten správný přijímač.
Našli jste v článku chybu?

31. 1. 2016 22:57

xkuxcaxpe (neregistrovaný)

Chtel bych se zeptat proc zrovna:
byte[] b = new byte[64]; //vytvořit buffer ?
Ma ta velikost 64 nejaky hlubsi vyznam?



9. 11. 2013 9:26

No ty sa pripajaš na HTTP server.
Preto musiš ešte zavolať metodu
"GET / HTTP/1.1" //vyziadas si dokument z rootovskeho adresara web servra
a ešte zadaš "Host: www.google.com" //cim specifikuješ v pripade virtualnych web servrov z ktoreho chceš dany dokument stiahnuť, ale aj keby si nemal virtualne servre tak v HTTP GET hlavicke sa tento udaj vyplna automaticky.

Tieto udaje už nie su sučasťou tohto telnetu ale metodami HTTP protokolu (hlavičiek), ktore sa vo web prehliadaci vyplňaju automatcky.…




120na80.cz: 5 nejčastějších mýtů o kondomech

5 nejčastějších mýtů o kondomech

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Měšec.cz: Komu musí od ledna zvýšit mzdu?

Komu musí od ledna zvýšit mzdu?

Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

Vitalia.cz: Vychytané vály a válečky na vánoční cukroví

Vychytané vály a válečky na vánoční cukroví

Vitalia.cz: Když přijdete o oko, přijdete na rok o řidičák

Když přijdete o oko, přijdete na rok o řidičák

Lupa.cz: Teletext je „internetem hipsterů“

Teletext je „internetem hipsterů“

Vitalia.cz: Pamlsková vyhláška bude platit jen na základkách

Pamlsková vyhláška bude platit jen na základkách

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

Root.cz: Certifikáty zadarmo jsou horší než za peníze?

Certifikáty zadarmo jsou horší než za peníze?

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

Vitalia.cz: Potvrzeno: Pobyt v lese je skvělý na imunitu

Potvrzeno: Pobyt v lese je skvělý na imunitu

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

Podnikatel.cz: Chtějte údaje k dani z nemovitostí do mailu

Chtějte údaje k dani z nemovitostí do mailu

Měšec.cz: mBank cenzuruje, zrušila mFórum

mBank cenzuruje, zrušila mFórum

Vitalia.cz: Jmenuje se Janina a žije bez cukru

Jmenuje se Janina a žije bez cukru

DigiZone.cz: Flix TV: dva set-top boxy za korunu

Flix TV: dva set-top boxy za korunu

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

120na80.cz: Horní cesty dýchací. Zkuste fytofarmaka

Horní cesty dýchací. Zkuste fytofarmaka