Predavanje br. index|1|2|3|4|5|6|7|8|9|10|11|12|13|14|HOME


u pripremi

Dvanaesto predavanje – mrežno programiranje

Portovi – protokoli – internet adrese – kreiranje InetAddress objekata – parsiranje InetAddress objekata – URL -


Portovi

U pravilu (koje ima mnogo iznimaka) svako računalo ima samo jednu Internet adresu. Međutim, svako računalo često treba komunicirati s više od jednog računala istodobno. Na primjer, u isto vrijeme može se odvijati nekoliko ftp sesija, nekoliko web konekcija, chat i tako dalje.

Da bi se to omogućilo, mrežno sučelje računala podijeljeno je na 65536 ulaza, takozvanih portova. Port je apstrakcija. To nije nikakvi fizički ulaz kao što su serijski ili paralelni portovi na osobnim računalima. No podaci putuju Internetom u paketima, pri čemu svaki paket nosi ne samo adresu hosta nego i broj porta na koji treba stići. Host će na osnovi specificiranog porta odrediti kojem programu je namijenjen dotični paket podataka.

Ako želite, IP adresu možete zamišljati kao uličnu adresu, a portove kao brojeve stanova u kućama. Routeri koji transportiraju pakete brinu se samo o “uličnoj adresi”, dakle ne čitaju broj porta. To je prepušteno lokalnom računalu.

Na Unixu potrebne su vam root privilegije za osluškivanje konekcija na portovima od 0 do 1023. Konekcije na portovima od 1024 do 65535 može osluškivati svatko, dok god određeni port nije zauzet (na istom portu ne može više od jednog programa istodobno osluškivati konekcije). Na operacijskim sustavima Windows NT, Windows 95 i Mac bilo koji korisnik može bez posebnih privilegija osluškivati bilo koji port.

Bilo koji udaljeni host može ostvariti konekciju na poslužitelj koji osluškuje neki port ispod 1024. Nadalje, višestruke simultane konekcije mogu se ostvariti na udaljeni host i udaljeni port. Na primjer, web poslužitelj koji osluškuje (u pravilu) na portu 80 može istovremeno obrađivati desetke konekcija istodobno, sve usmjerene na port 80.

Ukratko, samo jedan process na lokalnom hostu može koristiti neki port. Naprotiv, mnogo udaljenih hostova može ostvarivati konekcije na jedan te isti (udaljeni) port.

Mnogi servisi rade na tzv. općepoznatim (well-known), portovima. To znači da protokoli specificiraju da neki servis može ili mora koristiti određeni port. Na primjer, http poslužitelji obično osluškuju na portu 80, SMTP poslužitelji na portu 25, Echo poslužitelji na portu 7, a Discard poslužitelji na portu 9. Nemaju svi servisi općepoznate portove. Na primjer NFS dozvoljava da se portovi otkrivaju u vrijeme izvršavanja.


Protokoli

Neformalno govoreći, protocol definira kakodva hosta međusobno komuniciraju. U radio komunikaciji, na primjer, protokoli specificiraju da po završetku poruke treba reći “over” ili “kraj”. Kod računalnih mreža protokol definira što jest, a što nije prihvatljivo za jednog ili drugog sudionika komunikacije u određenom vremenskom trenutku.

Na primjer, daytime protokol, specificiran u RFC 867, kaže da se klijent povezuje s poslužiteljem na portu 13. Poslužitelj tada kaže klijentu točno vrijeme u formatu koji je za čovjeka čitljiv, a nakon toga prekida konekciju.

S druge strane, time protokol, specificiran u RFC 868, propisuje binarnu reprezentaciju vremena koja je čitljiva za računala.

Daytime i time šalju istu informaciju. Međutim, oni koriste različite formate i različite protokole da bi je poslali.

Postoji onoliko različitih vrsta protokola koliko ima servisa koji se njima koriste. Lockstep protokoli traže jedan odgovor za svaki upit. Neki protokoli kao što je FTP koriste višestruke konekcija. Većina koristi samo jednu. Neki protokoli, kao HTTP dozvoljavaju samo jedan upit i jedan odgovor po konekciji. Drugi, kao FTP, dozvoljavaju višestruke konekcije i više odgovora unutar svake konekcije.


Internet adrese

Svako računalo na Internetu identificira se pomoću jedinstvene, četverobajtne IP adrese. Ona se obično zapisuje u tzv. dotted quad formatu kao npr. 161.53.8.14 gdje je svaki byte neoznačena vrijednost između 0 i 255.

Budući da je ovakve brojeve teško zapamtiti, adrese se mapiraju u imena kao “student.math.hr”, “jagor.srce.hr” i tako dalje. Međutim, bitna je numerička adresa, ne ime.

Klasa java.net.InetAddress predstavlja takve adrese. Između ostalog, ona sadrži i metode za konvertiranje numeričkih adresa u imena hostova i obrunuto.

public static InetAddress getByName(String host) throws UnknownHostException
public static InetAddress[] getAllByName(String host) throws UnknownHostException
public static InetAddress getLocalHost() throws UnknownHostException
public boolean isMulticastAddress()
public String getHostName()
public byte[] getAddress()
public String getHostAddress()
public int hashCode()
public boolean equals(Object obj)
public String toString()

Kreiranje InetAddress objekata

Klasa InetAddress je malo neobična jer nema ni jedan public konstruktor. Umjesto toga ime hosta ili dotted quad adresu u string formatu prosljeđujete statičkoj metodi InetAddress.getByName() kao u sljedećem primjeru:

 
try {
  InetAddress public = InetAddress.getByName("public.srce.hr");
  InetAddress jagor = InetAddress.getByName("161.53.2.130");
}
catch (UnknownHostException e) {
  System.err.println(e);
}  
 

Neki hostovi imaju više adresa. Ako pretpostavljate da je to slučaj sa hostom kojeg ispitujete, možete dobiti polje objekata tipa InetAddress pomoću statičke metode InetAddress.getAllByName(), na primjer ovako:

 

import java.net.*;

 

public class HostAddresses {

 

  public static void main (String args[]) {

 

    try {

      InetAddress[] addresses = InetAddress.getAllByName(args[0]);

      for (int i = 0; i < addresses.length; i++) {

        System.out.println(addresses[i]);

      }

    }

    catch (UnknownHostException e) {

      System.out.println("Ne mogu naci trazeni host");

    }

    catch (ArrayIndexOutOfBoundsException e) {

      System.out.println("Upisite ime trazenog hosta");

    }

 

  }

 

}

 

Imat ćemo:

 
% javac HostAddresses.java
% java HostAddresses student.math.hr
student.math.hr/161.53.8.14
student.math.hr/161.53.29.70
%
 

Konačno, statička metoda InetAddress.getLocalHost() vraća objekt tipa InetAddress koji sadrži adresu računala na kojem se program izvršava:

 
try {
  InetAddress me = InetAddress.getLocalHost();
}
catch (UnknownHostException e) {
  System.err.println(e);
}  

Parsiranje InetAddress objekata

Za objekt tipa InetAddress možete dobiti ime hosta kao string, IP adresu kao string ili kao polje byteova, a također možete ispitati je li to tzv. multicast adresa (adresa klase D, čija su prva četiri bita 1110). To se rješava sljedećim metodama:

 
public String getHostName()
public String getHostAddress()
public byte[] getAddress()
public boolean isMulticastAddress()
 

Sljedeći program ispisuje podatke o lokalnom hostu.

 

import java.net.*;

 

public class LocalHost {

 

  public static void main(String[] args) {

 

    try {

      InetAddress me = InetAddress.getLocalHost();

      System.out.println("Ime lokalnog hosta  -----> " + me.getHostName());

      System.out.println("Adresa lokalnog hosta ---> " + me.getHostAddress());

      byte[] address = me.getAddress();

      System.out.print("Adresa po byteovima -----> ");

      for (int i = 0; i < address.length; i++) {

        System.out.print(address[i] + " ");

      }

      System.out.println();

      if (me.isMulticastAddress())

         System.out.println("multicast");

      else

         System.out.println("nije multicast");

    }

    catch (UnknownHostException e) {

      System.err.println("Ne mogu naci trazeni host");

    }

  }

 

}

 

% javac LocalHost.java
% java LocalHost
Ime lokalnog hosta  -----> student.math.hr
Adresa lokalnog hosta ---> 161.53.8.14
Adresa po byteovima -----> -95 53 8 14
nije multicast
%
 

Primijetite da su byteovi koje vraća metoda getAddress() označeni iako se, prema konvenciji, za dotted quad adrese koriste neoznačeni byteovi.


URL

URL, kratica za "Uniform Resource Locator", je način za jednoznačno identificiranje lokacije nekog resursa na internetu. Tipični URL-ovi izgledaju ovako:

 
http://public.srce.hr/
file:///Macintosh%20HD/Java/Docs/JDK%201.1.1%20docs/api/java.net.InetAddress.html#_top_
http://www.macintouch.com:80/newsrecent.shtml
ftp://ftp.carnet.hr/pub/
mailto:Mladen.Vedris@student.math.hr
telnet://student.math.hr
 

Većina URL-ova može se rastaviti na sljedeće komponente (koje ne moraju sve biti prisutne u svakom URL-u):


Klasa URL

URL-ovi su u Javi predstavljeni klasom java.net.URL. Postoje konstruktori za kreiranje novog URL-a i metode za parsiranje različitih dijelova URL-a. Ipak, bitni dio ove klase su metode koje vam omogućuju da sa nekog URL-a dobijete InputStream i na taj način čitate podatke s poslužitelja.

Klasa URL usko je povezana s handlerima protokola i sadržaja. Cilj je odvojiti snimljene (downloaded) podatke od protokola koji je korišten za njihovo snimanje. Handler protokola je odgovoran za komuniciranje sa poslužiteljem, tj. prenošenje byteova od poslužitelja do klijenta. On obavlja potrebne “pregovore” (negotiations) oko poslužitelja i svih potrebnih headera. Njegov je posao dobaviti byteove traženih podataka. Handler sadržaja preuzima te byteove i prevodi ih u neku vrstu Java objekta kao što je InputStream ili ImageProducer.

Kad konstruirate objekt tipa URL, Java će potražiti handler protokola koji razumije “protokolski” dio URL-a, kao što je npr. "http" ili "mailto". Ako ne pronađe takav handler, izbacit će MalformedURLException. Koji su protokoli podržani, ovisi o implementaciji, no http i file su podržani uglavnom svagdje. Sun-ov JDK 1.1. razumije sljedećih deset:

Posljednjih pet su specifični Sun-ovi protokoli i koriste ih interno JDK i HotJava.


Konstruiranje URL objekata

Pogledajmo neke od konstruktora klase URL. Svi oni izbacuju MalformedURLException.

 
public URL(String u) throws MalformedURLException
public URL(String protocol, String host, String file) throws MalformedURLException
public URL(String protocol, String host, int port, String file) throws MalformedURLException
public URL(URL context, String u) throws MalformedURLException
 

Za potpuni, apsolutni URL kao što je
http://student.math.hr/~vedris/java/java-predavanja/java-predavanje-01.htm
konstruirate odgovarajući URL objekt ovako:

 
  URL u = null;
  try {
    u = new URL("http://student.math.hr/~vedris/java/java-predavanja/java-predavanje-01.htm ");
  }
  catch (MalformedURLException e) {
  
  }
 

You can also construct the URL by passing its pieces to the constructor, like this:

 
  URL u = null;
  try {
    u = new URL("http", "student.math.hr", 
                  "~vedris/java/java-predavanja/java-predavanje-01.htm");
  }
  catch (MalformedURLException e) {
  
  }
 

U pravilu ne morate specificirati port za URL. Većina protokola ima pretpostavljeni (default) URL , pa tako za http pretpostavljamo 80. Međutim, ako tražimo nešto što nije na default portu, poslužit ćemo se trećim konstruktorom:

 
  URL u = null;
  try {
    u = new URL("http", "student.math.hr", 80, 
                  "~vedris/java/java-predavanja/java-predavanje-01.htm");
  }
  catch (MalformedURLException e) {
  
  }
 

Konačno, mnoge HTML datoteke sadrže relativne URL-ove. Na primjer, URL ove stranice je
http://student.math.hr/~vedris/java/java-predavanja/java-predavanje-12.htm
Ako bismo htjeli mirorirati ove stranice na nekom drugom računalu, mogli bismo umjesto apsolutnih koristiti relativne URL-ove koji nasljeđuju ime hosta itd.. Na primjer, ako na ovoj stranici imamo link java-predavanje-02, onda on zapravo pokazuje na http://student.math.hr/~vedris/java/java-predavanja/java-predavanje-02.htm, međutim, na računalu regoc.srce.hr pokazivao bi na
http://regoc.srce.hr/~vedris/java/java-predavanja/java-predavanje-12.htm
i tako dalje. Četvrti od gore navedenih konstruktora kreira URL koji je relativan u odnosu na zadani URL. Na primjer:

 
  URL u1, u2;
  try {
    u1 = new URL("http://student.math.hr/~vedris/java/java-predavanja/java-predavanje-01.htm");
    u2 = new URL(u1, "java-predavanje-02.htm");
  }
  catch (MalformedURLException e) {
  
  }
 

To je posebno korisni kod parsiranja HTML-a.


Parsiranje URL objekata

Klasa java.net.URL koristi sljedeće metode za rastavljanje URLa na njegove komponente:

 
public String getProtocol()
public String getHost()
public int getPort() 
public String getFile()
public String getRef()
 

Na primjer,

 
 try {
    URL u = new URL("http://student.math.hr/~vedris/java/html/TricksterApplet.html#top");
    System.out.println("Protokol: " + u.getProtocol());
    System.out.println("Host    : " + u.getHost());
    System.out.println("Port    : " + u.getPort());
    System.out.println("File    : " + u.getFile());
    System.out.println("Anchor  : " + u.getRef());
  }
  catch (MalformedURLException e) {
  
  }
 

Ako port u URLu nije eksplicitno specificiran, vraća se -1. To ne znači da se pokušava napraviti konekcija na (nepostojeći) port –1, nego jednostavno da se koristi default port.

Ako ne postoji referenca (anchor), onda je ona naprosto null, pa treba uhvatiti NullPointerException ili, još bolje, ispitati da je non-null prije nego li je koristimo.

Konačno, ako je izostavljen file, kao na primjer u http://student.math.hr, odgovarajuća se vrijednost postavlja na "/".


Čitanje podataka sa URLa

Metoda openStream() otvara konekciju na poslužitelj kojeg URL specificira te vraća InputStream s podacima iz te konekcije. To omogućuje snimanje podataka sa poslužitelja. Svi headeri koji dolaze prije stvarnih podataka ili tražene datoteke bit će obrisani prije nego stream bude otvoren. Dobit ćete samo čiste podatke.

 
public final InputStream openStream() throws IOException

Stream ćete pročitati koristeći se metodama iz paketa java.io o kojem smo govorili u desetom predavanju. Primijetite da većina mrežnih konekcija predstavlja manje pouzdan i sporiji izvor podataka nego što su to datoteke. Bit će dakle potrebno izvršiti bufferiranje, koristeći se pri tom klasama BufferedInputStream ili BufferedReader.

Program iz sljedećeg primjera čita niz URLa sa komandne linije. Iz svakog argumenta pokušava formirati URL, povezati se na specificirani poslužitelj i snimiti podatke koje će onda ispisati na System.out.

import java.net.*;
import java.io.*;
 
public class Webcat {
 
  public static void main(String[] args) {
 
    for (int i = 0; i < args.length; i++) {
      try {
        URL u = new URL(args[i]);
        InputStream is = u.openStream();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String theLine;
        while ((theLine = br.readLine()) != null) {
          System.out.println(theLine);
        }
      }
      catch (MalformedURLException e) {
        System.err.println(e);
      } 
      catch (IOException e) {
        System.err.println(e);      
      } 
      
    }
 
  }
 
}
 
% javac Webcat.java
% java Webcat "http://student.math.hr/~vedris/java/html/TricksterApplet.html"
<APPLET CODE="TricksterApplet.class"
CODEBASE="http://student.math.hr/~vedris/java/classes"
ARCHIVE="Trickster.jar"
WIDTH=1 HEIGHT=1>
</APPLET>
%

Utičnice (sockets)

Prije nego se podaci šalju preko Interneta s jednog hosta na drugi uz pomoć TCP/IP, oni se pakiraju u pakete različitih, ali konačnih veličina koji se nazivaju datagrami. Veličina datagrama varira od nekoliko desetaka byteova pa do oko 60,000 byteova. Sve što je veće od toga, a često i ono što je manje od toga, treba podijeliti u manje dijelove prije odašiljanja. Prednost takvog slanja podataka je u tome što ako se jedan paket putem izgubi, on se može poslati ponovo bez da se opet šalju svi ostali paketi. Također, ako paketi stignu izvan poretka, oni se mogu pravilno poredati i kod primatelja.

Ipak, sve je ovo transparentno za Java programera. Hostov ugrađeni mrežni softver će transparentno obaviti razdiobu podataka u pakete na strani pošiljatelja te ih ponovo spojiti na strani primatelja. Umjesto toga, Java programer se susreće s vrlo visokom apstrakcijom koju nazivamo utičnica (socket). Utičnica predstavlja pouzdanu konekciju za prijenos podataka između dva hosta. Ona vas izolira od detalja kodiranja paketa, gubitka i ponovnog slanja pošiljki, te uspostavljanja poretka među pristiglim paketima.

Utičnica obavlja sljedeće četiri fundamentalne operacije:

  1. uspostavljanje veze s udaljenim računalom
  2. slanje podataka
  3. primanje podataka
  4. prekidanje veze

Utičnica ne može biti priključena na više hostova istodobno.


Klasa Socket

Klasa java.net.Socket omogućuje izvođenje svih četiriju fundamentalnih operacija na utičnicama. Možete ostvariti konekciju na udaljeno računalo, slati i primati podatke te prekinuti konekciju.

Konekcija se obavlja uz pomoć konstruktora. Svakom objektu tipa Socket pridružen je točno jedan udaljeni host. Da biste ostvarili konekciju na drugi host, morate kreirati novi objekti tipa Socket.

public Socket(String host, int port) throws UnknownHostException, IOException
public Socket(InetAddress address, int port) throws IOException
public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException
public Socket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException
 

Slanje i primanje podataka obavlja se pomoću izlaznih i ulaznih streamova. Sljedeće metode daju odgovarajuće streamove za danu utičnicu.

 
public InputStream getInputStream() throws IOException
public OutputStream getOutputStream() throws IOException
 

Sljedeća metoda zatvara utičnicu:

 
public synchronized void close() throws IOException
 

Postoji i nekoliko metoda koje postavljanju različite opcije vezane uz utičnicu, no uglavnom ćete otkriti da su default vrijednosti sasvim zadovoljavajuće.

 
public void setTcpNoDelay(boolean on) throws SocketException
public boolean getTcpNoDelay() throws SocketException
public void setSoLinger(boolean on, int val) throws SocketException
public int getSoLinger() throws SocketException
public synchronized void setSoTimeout(int timeout) throws SocketException
public synchronized int getSoTimeout() throws SocketException
public static synchronized void setSocketImplFactory(SocketImplFactory fac) throws IOException
 

Sljedeće metoda daju različite informacije o utičnici:

 
public InetAddress getInetAddress()
public InetAddress getLocalAddress()
public int getPort()
public int getLocalPort()
 

Na kraju, tu je i uobičajena toString() metoda:

 
public String toString()

Konstruiranje Socket objekata

Pogledajmo public, non-deprecated konstruktore klase Socket.

 
public Socket(String host, int port) throws UnknownHostException, IOException
public Socket(InetAddress address, int port) throws IOException
public Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException
public Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException
 

Potrebno je, dakle, specificirati u najmanju ruku host i port na koji se želite konektirati. Host može biti specificiran bilo kao string, npr "student.math.hr" ili kao objekt tipa InetAddress. Port mora biti cijeli broj između 1 i 65535. Ne može biti bilo koji, već ga trebate znati isto kao i ime hosta. Na primjer,

 
Socket webStudent = new Socket("student.math.hr", 80); 
 

Posljednja dva konstruktora također specificiraju host i port sa kojega ostvarujete konekciju. Na sistemu sa više IP adresa, kao što su mnogi web poslužitelji, to vam omogućuje da odaberete svoje mrežno sučelje i IP adresu. Možete specificirati i broj porta, ali kako svaki pojedini port može biti zauzet, najbolje je staviti port 0. To će reći sistemu da izabere bilo koji slobodni port. Ako želite znati s kojeg porta ste napravili konekciju, pozvat ćete metodu getLocalPort().

 
Socket webStudent = new Socket("student.math.hr", 80, "pc-mladen.srce.hr", 0); 
 

Ovi konstruktori neće samo kreirati novi objekt tipa Socket. Oni će također pokušati ostvariti konekciju na specificirani udaljeni poslužitelj. Svi oni zato odbacuju IOException u slučaju da se konekcija iz bilo kojeg razloga ne može uspostaviti.


Port Scanner

Ne možete se naprosto konektirati na bilo koji port na bilo kojem hostu. Konekcija je moguća samo na one portove na kojima udaljeno računalo osluškuje dolazne konekcije. Konstruktore utičnica možete koristiti da biste ustanovili na kojim portovima računalo osluškuje. Pogledajte primjer:

 
import java.net.*;
import java.io.IOException;
 
public class PortScanner {
 
  public static void main(String[] args) {
 
    for (int i = 0; i < args.length; i++) {
      try {
        InetAddress ia = InetAddress.getByName(args[i]);
        scan(ia);
      }
      catch (UnknownHostException e) {
        System.err.println(args[i] + " is not a valid host name.");
      }
    }
 
  }
 
  public static void scan(InetAddress remote) {
 
    // Do I need to synchronize remote?
    // What happens if someone changes it while this method
    // is running?
 
    String hostname = remote.getHostName();
    // Skanirat cemo samo portove od 78 do 81. 
    // Potpuno skaniranje islo bi od 0 do 65535. Ne cinite to bez dozvole vlasnika hosta!
    for (int port = 78; port < 82; port++) {
      try {
        Socket s = new Socket(remote, port); 
        System.out.println("Server slusa na portu " + port
         + " racunala " + hostname);
        s.close();
      }
      catch (IOException e) {
        System.out.println("Racunalo ne slusa na portu " + port);
      }
    }
 
  }
 
  public static void scan(String remote) throws UnknownHostException {
 
    // Why throw the UnknownHostException? Why not catch it like I did
    // in the main() method?
    InetAddress ia = InetAddress.getByName(remote);
    scan(ia);
 
  }
 
}
 

Izvršit ćemo ovu ograničenu verziju PortScannera koja ispituje samo portove od 78 do 81. Primijetit ćete da računalo sluša na portu 80. To je standardni port za web poslužitelj.

 
% javac PortScanner.java
% java PortScanner localhost
Racunalo ne slusa na portu 78
Racunalo ne slusa na portu 79
Server slusa na portu 80 racunala localhost
Racunalo ne slusa na portu 81
% 
 

Upozorenje: Ne pokušavajte usmjeriti kompletni PortScanner prema mašini koja nije vaša vlastita bez dozvole vlasnika / sistem inženjera, jer će se to smatrati hakerskim napadom i ugrožavanjem sigurnosti računala!!!


Čitanje ulaza sa utičnice

Jednom kad je utičnica konektirana, možete slati podatke na poslužitelj putem izlaznog streama ili ih primati sa poslužitelja pomoću ulaznog streama. Koje točno podatke šaljete ili primate obično ovisi o protokolu.

Metoda getInputStream() vraća objekt tipa InputStream koji čita podatke iz utičnice. Možete koristiti uobičajene metode klase InputStream o kojima ste učili u desetom predavnju. Uglavnom će biti potrebno povezati InputStream sa nekim drugim ulaznim streamom ili readerom kako bi se lakše moglo rukovati podacima.

Na primjer, sljedeći fragment koda radi konekciju na daytime poslužitelj na portu 13 računala student.math.hr i ispisuje podatke koje od njega dobije.

   try {
      Socket s = new Socket("student.math.hr", 13);
      InputStream is = s.getInputStream();
      InputStreamReader isr = new InputStreamReader(is);
      BufferedReader br = new BufferedReader(isr);
      String theTime = br.readLine();
      System.out.println(theTime);
    }
    catch (IOException e) {
      return (new Date()).toString();
    }

Klijent Daytime

Pogledajmo sada kompletan program, Daytime klijent, koji se konektira na daytime poslužitelj računala student.math.hr na portu 13 i ispisuje podatke koje od njega dobije.

 
import java.net.*;
import java.io.*;
import java.util.Date;
 
 
public class Daytime {
 
  InetAddress server;
  int port = 13;
 
  public static void main(String[] args) {
 
    try {
      Daytime d = new Daytime("student.math.hr");
      System.out.println(d.getTime());
    }
    catch (IOException e) {
      System.err.println(e);
    }
 
  }
 
  public Daytime() throws UnknownHostException {
 
    this (InetAddress.getLocalHost());
 
  }
 
  public Daytime(String name) throws UnknownHostException {
    this(InetAddress.getByName(name));
  }
 
  public Daytime(InetAddress server) {
    this.server = server;
  }
 
  public String getTime() {
    if (server == null) return (new Date()).toString();
    try {
      Socket s = new Socket(server, port);
      InputStream is = s.getInputStream();
      InputStreamReader isr = new InputStreamReader(is);
      BufferedReader br = new BufferedReader(isr);
      StringBuffer sb = new StringBuffer();
      String theLine;
      while ((theLine = br.readLine()) != null) {
        sb.append(theLine + "\r\n");
      }
      return sb.toString();
    }
    catch (IOException e) {
      return (new Date()).toString();
    }
 
  }
 
}
 
% javac Daytime.java
% java Daytime
Tue Jan 22 16:16:31 MET 2002
%

Pisanje izlaza preko utičnice

Metoda getOutputStream() vraća objekt tipa OutputStream pomoću kojeg se ispisuju podaci na utičnicu. Možete koristiti sve uobičajene metoed klase OutputStream o kojima ste učili u desetom predavanju. Uglavnom će biti potrebno povezati OutputStream sa nekim drugim izlaznim streamom ili readerom kako bi se lakše moglo rukovati podacima.

Na primjer, sljedeći program se konektira na discard poslužitelj na portu 9 računala student.math.hr i šalje mu podatke koje čita sa standardnog ulaza System.in.

   byte[] b = new byte[128];
    try {
      Socket s = new Socket("student.math.hr", 9);
      OutputStream out = s.getOutputStream();
      while (true) {
        int n = System.in.available();
        if (n > b.length) n = b.length;
        int m = System.in.read(b, 0, n);
        if (m == -1) break;
        out.write(b, 0, n);
      }
      s.close();
    }
    catch (IOException e) {
    }

Klijent Discard

Pogledajmo sada kompletan program, Discard klijent, koji se konektira na Discard poslužitelj računala student.math.hr na portu 9 i šalje mu podatke koje čita sa standardnog ulaza System.in.

 
import java.net.*;
import java.io.*;
 
 
public class Discard extends Thread {
 
  InetAddress server;
  int port = 9;
  InputStream theInput;
 
  public static void main(String[] args) {
 
    try {
      Discard d = new Discard("student.math.hr");
      d.start();
    }
    catch (IOException e) {
      System.err.println(e);
    }
 
  }
 
  public Discard() throws UnknownHostException {
    this (InetAddress.getLocalHost(), System.in); 
  }
 
  public Discard(String name) throws UnknownHostException {
    this(InetAddress.getByName(name), System.in);
  }
 
  public Discard(InetAddress server) {
    this(server, System.in);
  }
 
  public Discard(InetAddress server, InputStream is) {
    this.server = server;
    theInput = System.in;
  }
 
  public void run() {
    byte[] b = new byte[128];
    try {
      Socket s = new Socket(server, port);
      OutputStream out = s.getOutputStream();
      while (true) {
        int n = theInput.available();
        if (n > b.length) n = b.length;
        int m = theInput.read(b, 0, n);
        if (m == -1) break;
        out.write(b, 0, m);
      }
      s.close();
    }
    catch (IOException e) {
    }
  }
 
}
 
% javac Discard.java
% java Discard
asdf
<Ctrl-C>
%

Čitanje i pisanje kroz utičnicu

Nije uobičajeno da se s utičnice samo čita ili da se na nju samo piše. Većina protokola traži da klijent i čita i piše. Neki protokoli traže da čitanje i pisanje bude naizmjenično, npr.

write
read
write
read
write
read
 

Drugi protokoli, npr. HTTP 1.0, traže višestruke ispise nakon kojih slijede višestruka čitanja, npr.:

 
write
write
write
read
read
read
read
 

Neki protokoli dozvoljavaju da klijentovi upiti i poslužiteljevi odgovori budu slobodno izmiješani.

Java ne stavlja nikakve restrikcije na čitanje i pisanje po utičnicama. Jedan thread može čitati s utičnice, a drugi može po njoj istodobno pisati. (Primijetite da to nije isto kao kad jedan thread čita datoteku, a drugi piše u nju.)


Čitanje i pisanje utičnice za HTTP

Program u sljedećem primjeru šalje upit HTTP poslužitelju koristeći izlazni stream utičnice, a zatim čita odgovor koristeći ulazni stream utičnice. HTTP poslužitelji sami zatvaraju konekciju kad pošalju odgovor.

 
import java.net.*;
import java.io.*;
 
 
public class Grabber {
 
  public static void main(String[] args) {
 
    int port = 80;
 
    for (int i = 0; i < args.length; i++) {
      try {
         URL u = new URL(args[i]);