13. diel - Java server - Propagácia lokálnou sieťou (3. časť)
V predchádzajúcom kvíze, Kvíz - Komunikačný protokol, Event bus a pluginy v Jave, sme si overili nadobudnuté skúsenosti z predchádzajúcich lekcií.
V minulej lekcii, Kvíz - Komunikačný protokol, Event bus a pluginy v Jave , sme naučili náš Java server ukazovať sa ďalším klientom v sieti. Dnes na túto komunikáciu naučíme našich klientov reagovať. Na strane klienta vytvoríme triedu, ktorá bude zachytávať údaje vysielané serverom.
Hľadanie lokálnych serverov
Nebude sa jednať o hľadaní v pravom slova zmysle, pretože servery budú
aktívne vysielať packety s informáciami o sebe. V module client
vytvoríme novú triedu LanServerFinder, do ktorej naimplementujeme
príjem packetov zo servera.
Implementácia triedy
Triedu necháme len implementovať rozhranie Runnable, aby si
klient mohol rozhodnúť sám, v akom vlákne kód pobeží:
public class LanServerFinder implements Runnable {}
Do triedy vložíme jednu inštančný premennú typu
MulticastSocket:
private final MulticastSocket socket;
Na tomto socketu budeme prijímať datagramy zo servera.
Ďalej pridáme dve inštančné premenné:
private OnServerFoundListener serverFoundListener; private boolean interrupt = false;
Triedu OnServerFoundListener vytvoríme za okamih. Premenná
interrupt má rovnaký význam, ako v predchádzajúcich
lekciách.
Konštruktor triedy bude mať dva parametre: 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 vytvoríme novú inštanciu triedy
MulticastSocket, ktorý bude počúvať na definovanom porte.
Metódou setSoTimeout() hovoríme, že každých päť sekúnd sa
vyvolá výnimka SocketTimeoutException. Pomocou metódy
joinGroup() nastavíme broadcastové adresu socketu.
Do triedy pridáme jednu verejnú metódu shutdown(), pomocou
ktorej budeme ukončovať beh vlákna:
public void shutdown() { interrupt = true; }
Ďalej pridáme Getter a setter pre listener
OnServerFoundListener:
public OnServerFoundListener getServerFoundListener() { return serverFoundListener; } public void setServerFoundListener(OnServerFoundListener serverFoundListener) { this.serverFoundListener = serverFoundListener; }
Teraz konečne vytvoríme vnútorné rozhranie
OnServerFoundListener:
@FunctionalInterface public interface OnServerFoundListener { void onServerFound(ServerStatusData data); }
Jedná sa o funkcionálne rozhranie s jednou metódou
onServerFound(), ktorú budeme volať vždy, keď príde nový
datagram s informáciami o stave servera.
Nakoniec implementujeme metódu 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čiatku metódy sa inicializuje čítacie buffer data na
veľkosť 1024 bajtov a príjmový datagramový packet
datagramPacket. V nekonečnej slučke sa kontroluje, či ak sa
nemá vlákno vypnúť. Ak je nastavený príznak premenné
interrupt na true, ukončí sa nekonečná slučka a
tým aj vlákno, v ktorom tento kód bežal. V tele slučky sa zavolá metóda
receive(), ktorá sa pokúsi prijať dáta zo servera. Metóda je
blokujúce, takže vlákno sa zablokuje na dobu, než prídu
dáta, alebo v našom prípade na dobu päť sekúnd, pretože
potom sa vyvolá výnimka SocketTimeoutException. Po úspešnom
prijatí dát sa vytvorí nová inštancia triedy
ByteArrayInputStream, do ktorej sa načítajú serializovaná dáta
o prijatej triede. Táto inštancia sa odovzdá ako parameter pri vytváraní
inštancie triedy ObjectInputStream. Z
ObjectInputStream u deserializujeme prijatú správu a přetypujeme
ju na triedu ServerStatusMessage. Z tejto triedy získame triedu
ServerStatusData. Nakoniec, ak bude nastavený
listener, sa zavolá metóda onServerFound() s
parametrom statusData. Tým zabezpečíme, že trieda bude iba
prijímať datagramy, ale ich spracovanie nechá na inej triede.
Na otestovanie funkčnosti by mal mať čitateľ dostatočné vedomosti. Ak si s niečím nebudete vedieť rady, sú vám k dispozícii komentáre pod lekcií.
Týmto by sme mali uzavretú implementáciu propagácie servera v lokálnej sieti. Nabudúce, v lekcii Java server - Vylepšenie systému pluginov , ešte zostaneme v serverovej časti. Vylepšíme si systém pluginov o načítanie externých pluginov a implementujeme prioritné inicializáciu pluginov.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 20x (176.3 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java
