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
ObjectInputStream
u 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