LINUXSOFT.cz
Nazwa użytkownika: Hasło:     
    CZ UK PL

> Java (18) - síťová komunikace I.

Jestliže jsem minule zdůrazňoval důležitost práce se soubory, nyní přijde na řadu možná ještě důležitější téma - výměna dat po síti. Podpora TCP/IP (včetně IPv6) je v Javě na výborné úrovni.

21.9.2005 06:00 | Lukáš Jelínek | czytane 45070×

RELATED ARTICLES KOMENTARZE   

Doba síťová

Dnešní dobu lze bez nadsázky označit za období síťové komunikace. Význam dálkového přenosu dat stále vzrůstá, proto je i v Javě věnována této oblasti mimořádná pozornost. Je to důležité hlavně proto, že právě sítě jsou častým místem pro nasazení Javy, ať pro stranu serverovou nebou klientskou.

Z tohoto důvodu jsem se rozhodl věnovat se této oblasti už nyní, v souvislosti se vstupně/výstupními operacemi. Začneme samozřejmě úplnými základy, tedy tím, jaká je vůbec filosofie síťové komunikace v Javě, jak se navazuje spojení, jak jsou reprezentovány adresy apod. "Vyšší stupeň" síťové komunikace (tedy podporu vzdáleného zpracování dat, přístup do databází, distribuované zpracování dat atd.) si necháme na později.

Základy síťové komunikace

Pro další výklad budu předpokládat, že čtenáři znají základy technologie TCP/IP verze 4, tedy zejména způsob reprezentace adres, základní vlastnosti jednotlivých komunikačních protokolů, mechanismus navazování a ukončování spojení atd., bude se to totiž zatraceně hodit. Protože IP verze 6 je poměrně nová záležitost, a zatím se moc nepoužívá, budu při popisu používat (tam, kde na tom záleží) vždy verzi 4 a o odlišnostech verze 6 se zmíním (většina API však verzi vůbec nerozlišuje). Nyní ještě raději velice stručně obecný úvod do TCP/IP:

Datagramová komunikace

Začneme tím jednodušším, datagramovou komunikací (reprezentovanou zde protokolem UDP). Kdo tuto komunikaci zná, tak ví, že vytvoříme balík dat (paket, datagram) a ten odešleme na nějaký (jiný, ale klidně i tentýž) počítač. Zde ho může (ale také nemusí) někdo očekávat - pokud ho nějaký program očekává, naslouchá na tzv. portu, a když mu data přijdou, zpracuje je. Komunikace probíhá prostřednictví naprosto nezávislých datagramů, které odesílatel posílá v dobré víře, že je někdo přijme a zpracuje. Není nijak zajištěno, že datagram dojde v pořádku (resp. že vůbec dojde), ani že datagramy přijdou v pořadí, v jakém se odesílaly. Pokud je to nutné, musí si tyto mechanismy zajistit příslušné aplikace.

V praxi se datagramová komunikace používá např. pro službu doménových jmen (DNS), některé způsoby multimediálních přenosů nebo třeba pro vzdálené volání procedur (RPC).

Spojová komunikace

Vyšším stupněm je spojová komunikace, zde protokolem TCP. Mezi počítači (přesněji řečeno porty na počítačích) se vytvoří komunikační kanál, který je zabezpečený z hlediska poškození nebo ztráty dat, a zaručuje správné pořadí přenášených dat. Data se sice posílají v paketech, ale to je aplikačním programům prakticky skryto. Před použitím je nutno komunikační kanál vytvořit, co předpokládá, aby jeden z komunikujících programů naslouchal na příslušném portu a druhý se na tento port připojil.

Spojovou komunikaci používá většina aplikací, jmenovitě třeba HTTP, FTP, poštovní protokoly (SMTP, POP3, IMAP4), telnet a SSH apod.

Adresy a porty v Javě

Než začneme komunikovat, musíme nejdřív vědět s kým. Proto je důležité vědět, jak se v Javě pracuje s adresami a porty. Není to nic těžkého, dokonce se dá říct, že oproti jiným jazykům (třeba C/C++) máme hodně práce usnadněno.

Reprezentace IP adres

Většina základních síťových záležitostí se nachází v balíku java.net. Tam také najdeme třídu InetAddress, která představuje obecnou IP adresu. V dřívějších verzích znamenala automaticky adresu IP verze 4, to už ale neplatí - existují potomci pro jednotlivé protokoly (Inet4Address a Inet6Address). Pro většinu použití na verzi nezáleží, proto metody přijímají instance této obecnější třídy.

Všechny uvedené třídy se vyznačují tím, že nemají žádný veřejný konstruktor. Instance se vytvářejí statickými metodami a lze přitom použít některou z následujících cest:

  • číselná reprezentace adresy - použije se metoda getByAddress(byte[] addr), která vytvoří instanci adresy IPv4 (předá-li se pole 4 hodnot) nebo IPv6 (pro pole 16 hodnot). Adresa se použije tak, jak je, žádný zpětný převod na jméno se nedělá. Pořadí bajtů je takové, jak jsme zvyklí (tedy nejvýznamnější bajt jako první).
  • číselná + jmenná reprezentace - metoda getByAddress(String host, byte[] addr). Umožňuje vycházet z číselné reprezentace s tím, že se uvede i jmenná adresa, aniž by se přitom prováděl DNS dotaz. Používání tohoto postupu je lépe se vyhnout.
  • jmenná reprezentace - adresa se zadá obvyklým způsobem metodě getByName(String host), ta provede DNS dotaz (přesněji řečeno, dotáže se systému, který může převody jmenných adres na číselné provádět různě) a vrátí instanci objektu Inet4Address nebo Inet6Address. Místo jmenné adresy lze uvést i číselnou (v textovém tvaru), ta se pak přímo použije (jako při volání metody getByAddress()).
  • získání lokální adresy - pokud potřebujeme lokální adresu, použijeme metodu getLocalHost(). Ovšem pozor, nemůžeme ovlivnit, kterou z lokálních adres metoda vrátí (pokud má počítač víc síťových rozhraní, může to být dost nepříjemné).

Vytvořená instance je neměnná. Adresa obecně nemusí znamenat pouze jediný cíl (unicast), ale může být i typu multicast. Zvláštním případem je tzv. nespecifikovaná (též anylocal) adresa, která se nesmí použít pro určení cíle, ale má význam při přidělování lokálních adres (říká, že lze použít libovolnou místní adresu, tedy i libovolné rozhraní).

Instance třídy InetAddress není pouze "kontejner na pár bajtů", ale má také vlastní funkcionalitu. Lze provádět reverzní DNS dotazy (getHostName() a getCanonicalHostName()) a dokonce zjistit dosažitelnost daného stroje (voláním některou z metod isReachable(); ovšem pozor na to, že správce zkoumaného cíle mohl používané služby, tedy ICMP ECHO a TCP ECHO, na firewallu zablokovat (tyto metody jsou k dispozici od JDK 1.5).

Jak se s objekty InetAddress pracuje, ukazuje příklad:

byte ip4[] = { 82, 208, 29, 37 };   // adresa IPv4

try {
    InetAddress ia = InetAddress.getByAddress(ip4);
    InetAddress ia2 = InetAddress.getByName("82.208.29.37");
    InetAddress ia3 = InetAddress.getByName("www.linuxsoft.cz");
    InetAddress ia4 = InetAddress.getByAddress("www.linuxsoft.cz", ip4);
    
    InetAddress local = InetAddress.getLocalHost(); // místní adresa
    
    System.out.println(ia.getCanonicalHostName());
    
    if (!ia.isReachable(5000)) System.err.println("Stroj je nedostupny");
} catch (UnknownHostException e) {
    e.printStackTrace();
}

Je to snad dostatečně zřejmé. První čtyři volání statických metod budou mít (pokud vše půjde, jak má) za následek vytvoření stejné instance (předpokládám, že www.linuxsoft.cz má jedinou IP adresu). Jinak by tomu bylo v situaci, když by např. nefungovala správně služba DNS - pak by třetí volání selhalo. Získání lokální adresy je jasné, stejně jako vypsání kanonického názvu pro první vytvořenou instanci (opět musí fungovat DNS). Poslední volání pak otestuje dostupnost daného stroje (s timeoutem 5 sekund).

Čísla portů

Jak známo, čísla portů jsou šestnáctibitová, tedy v rozsahu 0-65535. V Javě však používáme k jejich reprezentaci typ int, tedy číslo s mnohem větším rozsahem. Proto musíme dát pozor, abychom používali pouze platné hodnoty (pokud si pozor nedáme, dočkáme se obvykle výjimky IllegalArgumentException).

Úplná specifikace

Máme k dispozici i třídu, která sdružuje adresu a číslo portu do jediného objektu. Je to třída InetSocketAddress (je potomkem abstraktní třídy SocketAddress; to má svůj smysl, protože jako argument metod obvykle figuruje tato nadtřída, což umožňuje univerzálnější použití, třeba i pro jiné komunikační protokoly), vytvářená obvykle normálním způsobem, tj. voláním konstruktoru (kterému předáme potřebné informace).

Zvláštním způsobem vytvoření je zavolání statické metody createUnresolved(), která je k dispozici od JDK 1.5. Ta vytvoří instanci podle jmenné adresy, kterou se nesnaží přeložit na číselnou. To se hodí pro případy, kdy není k dispozici DNS, ale můžeme využít proxy server.

Následující příklad ukazuje různé cesty vytvoření objektů IP adresy s číslem portu:

try {
    // vytvoření z instance přeložené IP adresy
    InetAddress ia = InetAddress.getByName("www.linuxsoft.cz");
    InetSocketAddress isa = new InetSocketAddress(ia, 80);
    
    // dtto, ale nevyhazuje se výjimka při chybě DNS
    InetSocketAddress isa2 = new InetSocketAddress("www.linuxsoft.cz", 80);
    
    // nepřeložená adresa
    InetSocketAddress isa4 = InetSocketAddress.createUnresolved("www.linuxsoft.cz", 80);
    
    // nespecifikovaná adresa, automatický port
    InetSocketAddress isa3 = new InetSocketAddress(0);
    
} catch (UnknownHostException e) {
    e.printStackTrace();
}

První cestou je vytvořit instanci InetAddress a na jejím základě pak instanci adresy s portem. Druhá cesta (tedy předání jmenné adresy přímo konstruktoru InetSocketAddress) funguje podobně, avšak s tím rozdílem, že pokud se nepodaří jmennou adresu přeložit, nevyhodí se žádná výjimka a adresa zůstane nepřeložená. V této chybové situaci to tedy dopadne přesně tak, jak to udělá třetí z cest, tedy vytvoření nepřeložené adresy. A konečně poslední cestou je vytvoření nespecifikované ("wildcard") adresy, která se používá ve smyslu libovolné místní zdrojové adresy - zadává se pouze číslo portu. Ale v příkladu je jako toto číslo uvedena nula, která má zvláštní význam: systém přiřadí číslo zdrojového portu automaticky.

Síťová rozhraní

Jak jsem se zmínil v odstavci o získání lokální adresy, uvedená metoda neumožňuje určit, ke kterému síťovému rozhraní má daná adresa patřit - v počítači může být těchto rozhraní (síťových karet, modemů apod.) větší počet. Přesto ale nejsme zbaveni možnosti určit, se kterým rozhraním se bude pracovat. Máme totiž třídu NetworkInterface.

Rozhraní můžeme určit buď podle jeho IP adresy, anebo podle názvu zařízení, např. eth0 (to je sice platformově závislá věc, ale v mnoha případech jiné cesty není). Instance objektu se vytvářejí statickými metodami getByInetAddress() a getByName() (veřejné konstruktory nejsou k dispozici). Můžeme také zavolat metodu getNetworkInterfaces(), čímž získáme všechna aktivní rozhraní.

Hlavním smyslem třídy NetworkInterface je získání místní IP adresy. Je třeba si uvědomit, že každé rozhraní může mít přiřazeno více IP adres - k jejich zjištění slouží metoda getInetAddresses(), která vrátí jejich seznam. Pak už s nimi můžeme pracovat dle libosti. Více ukáže příklad:

try {
    // rozhraní identifikované názvem
    NetworkInterface ni = NetworkInterface.getByName("eth0");
    
    // zpracování seznamu IP adres k rozhraní
    Enumeration<InetAddress> adr = ni.getInetAddresses();
    while (adr.hasMoreElements()) {
        InetAddress ia = adr.nextElement();
        ...
    }        
} catch (SocketException e) {
    e.printStackTrace();
}

Z příkladu je jasně vidět, jak se získá přístup k rozhraní identifikovanému svým názvem, a jak se pak pracuje se seznamem IP adres, které má rozhraní přiřazeno. Většinou je tato adresa jediná, ale musíme počítat s tím, že jich může být víc, ale nemusí být také žádná (třeba při chybě v konfiguraci sítě).

Komunikujeme

Toto bylo nutné minimum, bez kterého se nelze obejít při jakékoli síťové komunikaci. Zatím jsme ještě nekomunikovali, ale k tomu se dostaneme příště. Budeme posílat UDP datagramy a navazovat spojení protokolem TCP.


KOMENTARZE
Pěkné 22.9.2005 13:26 Lukáš Zapletal
Re: Pěkné 26.9.2005 19:57 Lukáš Jelínek
K příkladu 1 17.8.2006 13:54 Martin Landa
Java ME 31.1.2010 09:54 Tomáš 'Elektron112' Velecký
  L Re: Java ME 31.1.2010 13:38 Aleš Hakl
    L Re: Java ME 31.1.2010 13:42 Tomáš 'Elektron112' Velecký
      L Re: Java ME 31.1.2010 14:23 Aleš Hakl
        L Re: Java ME 31.1.2010 14:27 Tomáš 'Elektron112' Velecký
Tylko zarejestrowani użytkownicy mogą dopisywać komentarze.
> Szukanie oprogramowania
1. Pacman linux
Download: 4875x
2. FreeBSD
Download: 9064x
3. PCLinuxOS-2010
Download: 8562x
4. alcolix
Download: 10945x
5. Onebase Linux
Download: 9660x
6. Novell Linux Desktop
Download: 0x
7. KateOS
Download: 6241x

1. xinetd
Download: 2412x
2. RDGS
Download: 937x
3. spkg
Download: 4758x
4. LinPacker
Download: 9963x
5. VFU File Manager
Download: 3197x
6. LeftHand Mała Księgowość
Download: 7202x
7. MISU pyFotoResize
Download: 2807x
8. Lefthand CRM
Download: 3562x
9. MetadataExtractor
Download: 0x
10. RCP100
Download: 3119x
11. Predaj softveru
Download: 0x
12. MSH Free Autoresponder
Download: 0x
©Pavel Kysilka - 2003-2024 | mailatlinuxsoft.cz | Design: www.megadesign.cz