NOVINKA! E-learningové kurzy umělé inteligence. Nyní AI za nejlepší ceny. Zjisti více:
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!

Lekce 13 - Java server - Propagace lokální sítí (3. část)

V minulé lekci, Java server - Propagace lokální sítí (2. část), jsme naučili náš Java server ukazovat se dalším klientům v síti.

Dnes na tuto komunikaci naučíme naše klienty reagovat. Na straně klienta vytvoříme třídu, která bude zachytávat data vysílaná serverem.

Hledání lokálních serverů

Nebude se jednat o hledání v pravém slova smyslu, protože servery budou aktivně vysílat packety s informacemi o sobě. V modulu client vytvoříme novou třídu LanServerFinder, do které naimplementujeme příjem packetů ze serveru.

Implementace třídy

Třídu necháme pouze implementovat rozhraní Runnable, aby si klient mohl rozhodnout sám, v jakém vlákně kód poběží:

public class LanServerFinder implements Runnable {

}

Do třídy vložíme jednu instanční proměnnou typu MulticastSocket:

private final MulticastSocket socket;

Na tomto socketu budeme přijímat datagramy ze serveru.

Dále přidáme dvě instanční proměnné:

private OnServerFoundListener serverFoundListener;
private boolean interrupt = false;

Třídu OnServerFoundListener vytvoříme za okamžik. Proměnná interrupt má stejný význam, jako v předchozích lekcích.

Konstruktor třídy bude mít dva parametry: broadcastAddress a port:

public LanServerFinder(InetAddress broadcastAddress, int port) throws IOException {
    this.socket = new MulticastSocket(port);
    this.socket.setSoTimeout(5000);
    this.socket.joinGroup(broadcastAddress);
}

V konstruktoru vytvoříme novou instanci třídy MulticastSocket, který bude poslouchat na definovaném portu. Metodou setSoTimeout() říkáme, že každých pět vteřin se vyvolá výjimka SocketTimeoutException. Pomocí metody joinGroup() nastavíme broadcastovou adresu socketu.

Do třídy přidáme jednu veřejnou metodu shutdown(), pomocí které budeme ukončovat běh vlákna:

public void shutdown() {
    interrupt = true;
}

Dále přidáme gettery a settery pro listener OnServerFoundListener:

public OnServerFoundListener getServerFoundListener() {
    return serverFoundListener;
}

public void setServerFoundListener(OnServerFoundListener serverFoundListener) {
    this.serverFoundListener = serverFoundListener;
}

Nyní konečně vytvoříme vnitřní rozhraní OnServerFoundListener:

@FunctionalInterface
public interface OnServerFoundListener {
    void onServerFound(ServerStatusData data);
}

Jedná se o funkcionální rozhraní s jednou metodou onServerFound(), kterou budeme volat vždy, když přijde nový datagram s informacemi o stavu serveru.

Nakonec implementujeme metodu run():

@Override
public void run() {
    final byte[] data = new byte[1024];
    final DatagramPacket datagramPacket = new DatagramPacket(data, data.length);

    while(!interrupt) {
        try {
            socket.receive(datagramPacket);
        } catch (SocketTimeoutException e) {
            continue;
        } catch (IOException e) {
            break;
        }

        final ByteArrayInputStream bais = new ByteArrayInputStream(
            datagramPacket.getData(),
            datagramPacket.getOffset(),
            datagramPacket.getLength());
        try {
            final ObjectInputStream ois = new ObjectInputStream(bais);
            final ServerStatusMessage statusMessage = (ServerStatusMessage) ois.readObject();
            final ServerStatusData statusData = (ServerStatusData) statusMessage.getData();
            if (serverFoundListener != null) {
                serverFoundListener.onServerFound(statusData);
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Na začátku metody se inicializuje čtecí buffer data na velikost 1024 bajtů a příjmový datagramový packet datagramPacket. V nekonečné smyčce se kontroluje, zda-li se nemá vlákno vypnout. Pokud je nastaven příznak proměnné interrupt na true, ukončí se nekonečná smyčka a tím i vlákno, ve kterém tento kód běžel. V těle smyčky se zavolá metoda receive(), která se pokusí přijmout data ze serveru. Metoda je blokující, takže vlákno se zablokuje na dobu, než přijdou data, nebo v našem případě na dobu pět vteřin, protože poté se vyvolá výjimka SocketTimeoutException. Po úspěšném přijetí dat se vytvoří nová instance třídy ByteArrayInputStream, do které se načtou serializovaná data o přijaté třídě. Tato instance se předá jako parametr při vytváření instance třídy ObjectInputStream. Z ObjectInputStreamu deserializujeme přijatou zprávu a přetypujeme ji na třídu ServerStatusMessage. Z této třídy získáme třídu ServerStatusData. Nakonec, pokud bude nastavený listener, se zavolá metoda onServerFound() s parametrem statusData. Tím zajistíme, že třída bude pouze přijímat datagramy, ale jejich zpracování nechá na jiné třídě.

Na otestování funkčnosti by měl mít čtenář dostatečné znalosti. Pokud si s něčím nebudete vědět rady, jsou vám k dispozici komentáře pod lekcí.

Tímto bychom měli uzavřenou implementaci propagace serveru v lokální síti.

V následujícím kvízu, Kvíz - Propagace lokální sítí v Javě, 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 19x (176.3 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

Předchozí článek
Java server - Propagace lokální sítí (2. část)
Všechny články v sekci
Server pro klientské aplikace v Javě
Přeskočit článek
(nedoporučujeme)
Kvíz - Propagace lokální sítí v Javě
Článek pro vás napsal Petr Štechmüller
Avatar
Uživatelské hodnocení:
Ještě nikdo nehodnotil, buď první!
Autor se věnuje primárně programování v Javě, ale nebojí se ani webových technologií.
Aktivity