Image
22.6.2016 0 Comments

Java pod lupou I. /14.časť

Späť na úvod >> Späť na programovanie >> Späť na seriál

Balíky java.util.* a java.net

Opis balíka java.util by nebol úplný, keby sme vynechali jeho dva podbalíky java.util.zipjava.util.jar. Tretím balíkom, ktorému sa v tomto čísle pozrieme na zúbky, je java.net.

Balík java.util.zip

Tento balík v zhode so svojím názvom obsahuje triedy pre čítanie a zápis štandardných súborov ZIP a GZIP, triedy pre kompresiu a dekompresiu dát pomocou algoritmu DEFLATE (použitého v súboroch ZIP a GZIP) a nakoniec triedy pre výpočet kontrolného súčtu podľa algoritmov CRC-32Adler-32 (špecifikácie formátov a algoritmov možno nájsť v dokumentoch RFC 1950–1952).

Začneme od konca. Triedy Adler32CRC32 slúžia na výpočet kontrolného súčtu postupnosti bajtov. Práca s nimi spočíva v opakovanom volaní metódy update(), ktorej argumentom je bajt alebo pole bajtov. Po odoslaní všetkých bajtov postupnosti získame kontrolný súčet metódou getValue(). Tretia metóda reset() kontrolný súčet „reštartuje“ na úvodnú hodnotu a dovoľuje tak opätovne použiť existujúcu inštanciu triedy. Schéma použitia oboch tried vyzerá približne takto:

CRC32 crc = new CRC32();
while ( ... )
{
  byte b = ... ;
  crc.update(b);
}
long chksum = crc.getValue();

Triedy implementujú rozhranie Checksum, ktoré obsahuje všetky uvedené metódy.

Dve ďalšie triedy CheckedInputStreamCheckedOutputStream predstavujú vstupný, resp. výstupný prúd s pridanou schopnosťou počítať kontrolný súčet dát, ktoré ním prechádzajú. Obe triedy sú odvodené od „filtračných“ tried FilterInputStreamFilterOutputStream (pozri predminulú časť seriálu) a navyše obsahujú metódu getChecksum(), ktorá vracia aktuálnu hodnotu kontrolného súčtu.

Kompresiu dát použitím populárnej knižnice ZLIB zabezpečuje trieda Deflater. Metódami setDictionary(), setStrategy()setLevel() môžeme nastaviť počiatočný slovník, komprimačnú stratégiu a úroveň kompresie (trieda Deflater obsahuje niekoľko preddefinovaných úrovní vo forme statických konštánt). Údaje, ktoré chceme komprimovať, posielame triede pomocou metódy setInput(), skomprimované údaje získame volaním metódy deflate(). Pomocou metódy needsInput() zistíme, či treba dodať ďalšie vstupné údaje, metódou finish() oznámime triede, že už stačilo, ďalšie údaje jej nedodáme, a aby kompresiu vhodne ukončila. Tento stav indikuje metóda finished() vrátením hodnoty true. Vďaka metódam getTotalIn(), getTotalOut()getAdler() máme k dispozícii celkový počet bajtov, ktoré „pretiekli“ triedou, a ich kontrolný súčet (Adler-32). Konečne metódy reset()end() slúžia na reinicializáciu, resp. ukončenie práce s triedou Deflater.

Opačnú operáciu, dekompresiu dát, má na starosti trieda Inflater. Aj tu vstupné údaje zadávame pomocou metódy setInput(), ich dekomprimovanú podobu získame volaním metódy inflate(). Na zadanie počiatočného slovníka použijeme metódu setDictionary() (či je to nevyhnutné, zistíme metódou needsDictionary()). Metódy needsInput(), finished(), getTotalIn(), getTotalOut(), getAdler(), reset()end() majú rovnaký význam ako pri predošlej triede. Či vo vstupnom bufferi po dekompresii zostali nejaké nespracované dáta, zistíme pomocou metódy getRemaining().

Triedu Deflater použijeme obyčajne takýmto spôsobom:

Deflater dft = new Deflater();
byte[] data = new byte[...];
byte[] cdata = new byte[...];
...
dft.setInput(data);
dft.finish();
dft.deflate(cdata);

a triedu Inflater zase takto:

Inflater ift = new Inflater();
ift.setInput(cdata);
ift.inflate(data);

Triedy Deflater a Inflater sú vhodne skombinované so vstupnými/výstupnými prúdmi v triedach DeflaterOutputStreamInflaterInputStream (opäť sú to „filtračné“ triedy). Prvá z oboch tried disponuje schopnosťou priebežne komprimovať údaje zapisované pomocou štandardnej metódy write(), druhá, naopak, dokáže dekomprimovať údaje čítané pomocou metódy read(). Na detekciu konca vstupného prúdu InflaterInputStream použijeme metódu available().

Štvorica tried ZipEntry, ZipFile, ZipOutputStreamZipInputStream značným spôsobom uľahčuje prácu so súbormi ZIP. ZipEntry reprezentuje jeden položku súboru ZIP (t. j. jeden zo súborov v archíve). Každá položka má svoje meno, ktoré sa zadáva ako argument konštruktora a zistiť ho možno pomocou metódy getName(). Ďalšie metódy triedy ZipEntry slúžia na nastavenie/sprístupnenie rôznych atribútov položky: setTime(), setSize(), setCompressedSize(), setCrc(), setMethod(), setExtra(), setComment() a ich get***() ekvivalenty – názvy metód sú dostatočne opisné. Pomocou metódy isDirectory() zistíme, či je daná položka adresárom (názvy adresárov sa končia znakom /).

Ďalšia trieda ZipFile slúži na čítanie položiek zo súboru ZIP. Argumentom jej konštruktora je reťazec s cestou k súboru alebo objekt triedy File. Cestu k súboru môžeme spolu s menom zistiť dodatočne volaním metódy getName(). Metóda size() vracia počet položiek v súbore, metóda entries() vracia položky ako enumeráciu (typ Enumeration). Pomocou getEntry() sa dostaneme k vybranej položke a pomocou getInputStream()získame vstupný prúd, ktorý možno použiť na čítanie údajov z položky. Metóda close() otvorený súbor korektne uzavrie.

Trieda ZipOutputStream je potomkom DeflatedOutputStream a predstavuje výstupný prúdový filter na zápis do súborov ZIP. Komprimačnú metódu, úroveň kompresie a prípadný komentár môžeme špecifikovať pomocou metód setMethod(), setLevel() setComment(). Novú položku v súbore ZIP začneme metódou putNextEntry(). Zápis sa realizuje klasickou metódou write(), položku uzavrieme volaním metódy closeEntry(). Súbor (a prúd) uzavrieme metódou close().

Komplementárna trieda ZipInputStream slúži ako vstupný filter na čítanie zo súborov ZIP. Je potomkom triedy InflaterInputStream a pracuje sa s ňou podobne ako s predchádzajúcou triedou: nasledujúcu položku „začneme“ volaním getNextEntry(), čítanie obsahu položky obstará metóda read() a položku nakoniec uzavrieme metódou closeEntry(). Metóda createZipEntry() slúži na vytvorenie objektu ZipEntry pre položku so zadaným menom.

Posledné dve triedy, GZIPOutputStreamGZIPInputStream, predstavujú prúdové filtre pre zápis a čítanie do/z GZIP súborov. Tie nemajú žiadnu vnútornú štruktúru (preto sa v UNIX-like operačných systémoch musí použiť najprv TAR archivátor), takže sú predefinované len metódy read()write() (triedy sú odvodené od DeflaterOutputStreamInflaterInputStream).

Balík java.util.zip obsahuje aj dve triedy výnimiek: DataFormatException, ktorá signalizuje chybu formátu údajov pri ich rozbaľovaní, a ZipException, ktorá indikuje chybu pri práci so súborom ZIP.

Balík java.util.jar

Druhý podbalík poskytuje triedy pre prácu so súbormi JAR (= Java ARchive), ktoré sú založené na štandardnom formáte ZIP a navyše voliteľne obsahujú tzv. manifestačný súbor (manifest file, ďalej len „manifest“). Tento súbor obsahuje metainformácie o obsahu súboru JAR vo forme položiek a k nim priradených množín atribútov (pod atribútom treba rozumieť dvojicu kľúč–hodnota).

Upozornenie: nasledujúce riadky budú čitateľovi omnoho jasnejšie po preštudovaní dokumentácie k JDK, kde sa nachádza opis súborov JAR i manifestu.

Prvou z tried balíka java.util.jar je Attributes, ktorá funguje podobne ako mapovacie triedy balíka java.util – mapuje názvy atribútov na reťazcové hodnoty pridružené k nim (trieda Attributes v skutočnosti implementuje rozhranie java.util.Map). Hodnotu vybraného atribútu zistíme pomocou metódy getValue(). Meno atribútu môžeme zadať ako reťazec alebo ako objekt typu Attributes.Name, čo je pomocná trieda na reprezentáciu názvov atribútov s množstvom preddefinovaných statických konštánt (bližšie pozri dokumentáciu k JDK). Opačnú operáciu – nastavenie hodnoty atribútu – realizuje metóda putValue(). Ostatné metódy boli opísané v predchádzajúcej časti.

Samotný manifest reprezentuje trieda Manifest. Pomocou metódy getMainAttributes() získame tzv. hlavné atribúty manifestu (týkajú sa celého manifestu) a pomocou getAttributes() sa dostaneme k atribútom jednotlivých položiek. Zoznam položiek (objekt typu Map) získame volaním metódy getEntries(). Metóda clear() zruší celý obsah manifestu, metódy read()write() slúžia na načítanie/zápis obsahu manifestu z/do zadaného prúdu.

Nasledujúce štyri triedy – JarEntry, JarFile, JarOutputStreamJarInputStream – sú významom prakticky totožné so svojimi náprotivkami v balíku java.util.zip (ba čo viac, sú ich potomkami). Trieda JarEntry reprezentuje položku súboru JAR (pozor, nie položku manifestu!) a obsahuje navyše metódu getAttributes(), ktorá vracia atribúty danej položky, a getCertificates() – tá vracia digitálne certifikáty položky.

Trieda JarFile slúži na čítanie obsahu súboru JAR. Je možné prikázať, aby sa pri otváraní súboru skontroloval jeho digitálny podpis. Medzi pridanými metódami nájdeme getManifest() na prístup k manifestu súboru JAR a getJarEntry() na sprístupnenie zadanej položky súboru.

Triedy JarOutputStreamJarInputStream celkom logicky slúžia ako prúdové filtre na zápis/čítanie súborov JAR. V prvej triede, JarOutputStream, nenájdeme nič nové, len predefinovanú metódu putNextEntry(). Druhá trieda, JarInputStream, obsahuje dve nové metódy: getManifest()getNextJarEntry(). Ich význam je zrejmý.

V balíku java.util.jar nájdeme jednu výnimku, JarException, odvodenú od ZipException a indikujúcu problémy pri čítaní alebo zápise súboru JAR.

Balík java.net

Z názvu tohto balíka je na prvý pohľad jasné, že bude obsahovať triedy pre podporu sieťových aplikácií. Komunikácia sa realizuje prostredníctvom soketov – Java mlčky predpokladá prítomnosť TCP/IP (čo je však úplne logické, majorita súčasných sietí používa protokoly rodiny TCP/IP).

Na reprezentáciu adries IP (protokolu IPv4) je k dispozícii trieda InetAddress. Čo je zvláštne, táto trieda nemá nijaký konštruktor (teda aspoň nie verejne prístupný), na vytvorenie objektu typu InetAddress treba použiť jednu zo statických metód getLocalHost(), getByName(), getAllByName(). Prvá z metód vracia objekt reprezentujúci lokálnu adresu IP, druhá a tretia vracajú objekt, resp. pole objektov predstavujúcich jednu adresu IP alebo množinu všetkých možných adries IP počítača špecifikovaného pomocou reťazca s doménovým menom alebo s adresou IP v bodkovom zápise (ako napr. "195.168.1.4").

Medzi ďalšie metódy triedy InetAddress patria: isMulticastAddress() – slúži na rozpoznanie multicast adries (to sú adresy triedy D, začínajúce sa bitmi 1110), getHostName() – vracia doménové meno patriace k danej adrese IP, getHostAddress() – vracia adresu IP ako reťazec v bodkovom zápise a getAddress() – vracia adresu IP ako pole štyroch bajtov.

Význam triedy URL je zrejmý. Na zopakovanie: URL (= Uniform Resource Locator) predstavuje spôsob, ako na internete možno odkazovať na rôzne prostriedky (ako napr. stránky, súbory, mailové schránky). URL pozostáva zo špecifikácie protokolu, názvu cieľového počítača, voliteľného čísla portu, na ktorom prebieha komunikácia, a cesty, ktorá jednoznačne identifikuje prostriedok. Trieda URL obsahuje viacero konštruktorov, novú inštanciu možno zostrojiť napríklad takto:

URL url = new URL("http", "www.niekde.sk", 80, "/index.htm");

ale aj takto:

URL url = new URL("http://www.niekde.sk:8080/index.htm");

Metódy getQuery(), getPath(), getUserInfo(), getAuthority(), getPort(), getProtocol(), getHost(), getFile()getRef() vracajú rozličné zložky URL (pod query sa rozumie časť cesty za prípadným znakom ?, authority je zloženina host:port, resp. user@host:port, a ref je časť cesty za prípadným znakom #). Metódou sameFile() môžeme zisťovať zhodu dvoch URL s vylúčením zložky ref. Pomocou metódy toExternalForm() získame URL v tvare súvislého reťazca. Dôležitá metóda openConnection() vráti objekt URLConnection (pozri ďalej), ktorý reprezentuje sieťové spojenie so zadaným URL. Podobne metóda openStream() vytvorí spojenie s URL a vráti vstupný prúd na čítanie z tohto spojenia. Pomocou metódy getContent() získame obsah URL vo forme javovského objektu.

Komunikačné spojenie medzi javovskou aplikáciou a URL zabezpečuje abstraktná trieda URLConnection. Konštruktor tejto triedy je chránený, nové objekty získame volaním metódy openConnection() nad objektom typu URL. Trieda obsahuje množstvo metód, z tých významnejších vyberáme: connect() – fyzicky zrealizuje požadované spojenie (teda prepojí obe komunikujúce strany), getContentLength()getContentType(), getContentEncoding(), getExpiration(), getDate() a getLastModified() – vracajú rovnako pomenované polia hlavičky, nachádzajúcej sa na začiatku odpovede vzdialeného počítača (spomeňme si hoci na polia štandardnej hlavičky HTTP), veľmi dôležitá metóda getContent() – vracia samotný dátový obsah URL, getInputStream()/getOutputStream() – vracajú prúdy na čítanie alebo zápis z/do spojenia, a mnoho ďalších.

Java poskytuje dve inkarnácie triedy URLConnection – pre HTTP spojenia je to HttpURLConnection a pre spojenia k  archívom JAR zase JarURLConnection. Obe triedy sú abstraktné, takže skutočná implementácia spojení je záležitosťou konkrétneho JVM. V triede HttpURLConnection nájdeme množstvo statických finálnych členov, reprezentujúcich stavové kódy protokolu HTTP a niekoľko špecifických metód: setFollowRedirects() – zapína či vypína automatické presmerovanie (pri vrátení kódu 3xx), setRequestMethod() – nastavuje prístupovú metódu (GET, POST a pod.), getResponseCode(), getResponseMessage() – vracajú návratový kód, resp. návratovú správu HTTP protokolu, ako aj niekoľko ďalších, menej významných metód.

Druhá trieda JarURLConnection sa dostáva k slovu pri prístupe k súborom JAR – URL má vtedy formát jar:http://www.niekde.sk/archiv.jar!/sk/niekde/foo/bar.class (ako vidieť, za výkričníkom sa nachádza cesta ku konkrétnej triede – položke súboru JAR). Z metód triedy spomenieme getEntryName() – vracia názov sprístupňovanej položky, getJarFile(), getJarEntry(), getManifest(), getAttributes() – vracajú objekty typu JarFile, JarEntry, Manifest, Attributes (pozri balík java.util.jar).

Príklad, ako vypísať HTML obsah vybranej webovej stránky (obsah, pochopiteľne, nie je nijako interpretovaný):

URL url = new URL("http://www.pcrevue.sk/");
URLConnection uc = url.openConnection();
uc.connect();
BufferedReader br =
    new BufferedReader(
    new InputStreamReader(uc.getInputStream()));
 
while (true)
{
  String line = br.readLine();
  if (line == null)
    break;
  System.out.println(line);
}

Všimnite si, že sa vôbec nestaráme o to, akého typu je objekt uc v skutočnosti.

Okrem komunikácie so zadaným URL možno v Jave vytvoriť spojenie priamo medzi dvoma soketmi (soket je koncový komunikačný bod, identifikovaný adresou IP a číslom portu). Trieda Socket reprezentuje klientsky soket pre prúdovú komunikáciu pomocou TCP. Pri jej konštrukcii môžeme zadať cieľovú adresu a port (soket sa potom automaticky spojí so svojím náprotivkom) a aj lokálnu adresu a port (to keď chceme „vysielať“ z konkrétneho portu). Metódami getInetAddress(), getLocalAddress(), getPort()getLocalPort() zistíme vlastnosti soketu, pomocou getInputStream()getOutputStream() získame prúdy na čítanie a zápis z/do soketu. Metóda close() ukončí komunikáciu a soket uzavrie. Niekoľko ďalších metód slúži na nastavenie parametrov komunikácie. Zaujímavé je, že samotnú komunikáciu realizuje objekt typu SocketImpl (abstraktná trieda). Pokiaľ si však nechceme napísať vlastnú implementáciu soketov, nie je táto trieda zaujímavá.

Ak programujeme serverovú časť aplikácie, použijeme triedu ServerSocket. Argumentom jej konštruktora je číslo portu, na ktorom bude soket počúvať. Najdôležitejšou metódou je accept(), ktorej volanie je blokujúce – metóda čaká, až sa na soket pripojí klient „zvonka“. V tom okamihu vracia objekt typu Socket, pomocou ktorého možno s klientom komunikovať štandardným spôsobom. Z ďalších metód medzi tie zaujímavejšie patrí getInetAddress(), getLocalPort()close() s významom rovnakým ako v triede Socket.

Ako vyzerá kostra serverovej aplikácie, možno lepšie pochopiť z nasledujúceho príkladu:

ServerSocket ss = new ServerSocket(12345);
while (true)
{
  Socket cs = ss.accept();
  BufferedReader br =
      new BufferedReader(
      new InputStreamReader(cs.getInputStream()));
  while (true)
  {
    String ln = br.readLine();
    if (ln == null)
      break;
    System.out.println(ln);
  }
  cs.close();
}

(Kompletnejšiu verziu nájde čitateľ spolu s ostatnými príkladmi na webe PC REVUE.)

V príklade vytvoríme serverový soket ss, počúvajúci na porte č. 12345. V nekonečnom cykle pomocou metódy accept() získame klientsky soket cs, z ktorého čítame riadky textu a vypisujeme na konzolu. Na otestovanie je potrebné pripojiť sa k počítaču, na ktorom program beží, napríklad pomocou telnetu (ale na porte 12345!). Posielané riadky sa budú postupne objavovať na konzole. Program sa ukončí stlačením Ctrl+C.

Na datagramový prenos pomocou protokolu UDP (bez spojenia, nezabezpečený) slúži trieda DatagramSocket. Pri jej konštruovaní môžeme zadať číslo lokálneho portu soketu. Pripojenie k zadanej vzdialenej adrese (pozor, nie vytvorenie sieťového spojenia ako pri TCP) má na starosti metóda connect(), odpojenie metóda disconnect(). Na príjem a odosielanie údajov slúžia metódy send()receive(), soket uzavrieme volaním close(). Ostatné metódy sú podobné ako v triede Socket, implementáciu soketov zabezpečuje trieda DatagramSocketImpl.

Keďže v prípade UDP nejde o prúdový prenos, nepracujeme s prúdmi, ale s objektom triedy DatagramPacket (ten je ostatne argumentom oboch metód – send() aj receive()). Pri jeho konštruovaní zadávame pole bajtov, ktoré bude fungovať ako prijímací alebo vysielací buffer, a pri odosielaných datagramoch aj cieľovú adresu. Pomocou metód triedy DatagramPacket môžeme nastavovať a zistiť zdrojovú, resp. cieľovú adresu a port, dĺžku údajov a samotné údaje.

Na záver spomeňme ešte výnimky: MalformedURLException (chyba v syntaxi URL), ProtocolException (chyba na úrovni prenosového protokolu), SocketException s potomkami (chyba pri práci so soketmi), UnknownHostException (chyba pri preklade pomocou DNS) a UnknownServiceException (pokus o prácu s neznámym MIME typom).

Žiaľ, na viac z balíka java.net nemáme miesto. Zvyšné triedy slúžia napríklad na implementáciu multicast soketov (MulticastSocket), zabezpečenie prístupových práv (SocketPermission, NetPermission, Authenticator), prácu s URL a interpretáciu obsahu (ContentHandler, URLStreamHandler) či konverziu medzi typom String a MIME typom x-www-form-urlencoded (URLEncoder, URLDecoder). Záujemca si viac informácií isto nájde priamo v dokumentácii.

 

 

 


Nechajte si posielať prehľad najdôležitejších správ emailom

Mohlo by Vás zaujímať

Ako na to

Tipy a triky: Ako na snímku obrazovky na akomkoľvek počítači s Windows?

02.12.2016 00:13

Ak snímky obrazovky robíte často apotrebujete napríklad funkcie na posun stránok alebo snímanie zobrazenia pri vyššom rozlíšení displeja, zrejme používate nejakú špecializovanú aplikáciu. Väčšina použ ...

Ako na to 1

Tipy a triky: Ako aplikácii prednastaviť spúšťanie s administrátorskými právami?

30.11.2016 00:10

Väčšina aspoň trochu skúsenejších používateľov vie, že aj keď máte na operačnom systéme Windows vytvorený administrátorský účet, aplikácie pre bezpečnosť nefungujú vždy splnými administrátorskými práv ...

Ako na to 2

Tipy a triky: Ako vypnúť uzamykaciu obrazovku vo Windows 10?

29.11.2016 00:10

Rozčuľuje vás, že pred každým prihlásením doúčtu vášho počítača musíte prejsť uzamykacou obrazovkou? Windows 10 na tejto obrazovke ukazuje čas,dátum anejakú zaujímavú fotografiu zrôznych kútov sveta. ...

Žiadne komentáre

Vyhľadávanie

Kyocera - prve-zariadenia-formatu-a4-s-vykonom-a3

Najnovšie videá