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 20x (176.3 kB)
Aplikace je včetně zdrojových kódů v jazyce Java
