Verkko-ohjelmointi

Luento 3

Protokolla

Protokolla on sovittu tapa kommunikoida kahden tai useamman tietokoneen välillä. Tällä kurssilla keskitymme lähinnä TCP/IP -protokollaan ja HTTP -protokollaan.
 

TCP/IP -protokolla

TCP/IP -protokolla koostuu kahdesta eri protokollasta TCP (Transmission Control Protocol) ja IP (Internet Protocol).  TCP/IP -protokollassa on viisi kerrosta: Jokaisella kerroksella on oma tarkoituksensa.

TCP -protokolla vastaa tiedon luotettavasta siitämisestä kohteiden välillä. TCP on yhteydellinen protokolla jossa tiedon perille meno varmistetaan. TCP:n sijasta voi käyttää nopeampaa UDP-protokollaa (User datagram protocol), joka ei varmista kaiken tiedon perillemenoa. Osa tiedosta saattaa siis hukkua matkalle UDP -protokollassa, mutta esimerkiksi videokuvan lähetyksessä se ei ole merkityksellistä.

IP -protokolla huolehtii pakettien reitittämisestä internetissä. Olennainen osa IP -protokolla toimintaa on nelitavuinen IP-osoite, joka kertoo paketin kohteen, esimerkiksi 192.168.175.31. Jokainen IP-osoitteen numero on väliltä 0 ... 255. Tietokoneet ja verkkolaitteet tunnistetaan internetissä IP-osoitteen avulla. Joillakin IP-osoitteilla on tietty erikoismerkitys, kuten

127.0.0.l localhost, eli paikallinen kone (local loop back).

Eri IP-osoitteiden näkyvyyttä säädellään aliverkon peitteellä (aliverkon maski) joka määrää, mitkä IP-osoitteet ovat kunkin laitteen nnäkökulmasta mielenkiintoisia, ja mitkä osoitteet eivät.

TCP -paketteja voi linux -ympäristössä nuuskia esim. sniffit -nimisellä ohjelmalla.  http://reptile.rug.ac.be/~coder/sniffit/sniffit.html
Vastaavaan käyttön Windows -ympäristössä löytyy esimerkiksi Analyzer -niminen ohjelma osoitteesta:
http://netgroup-serv.polito.it/analyzer .

Portti

Yleensä tietokone on yhteydessä verkkoon vain yhdellä fyysisellä yhteydellä (yhdellä verkkokortilla, modeemilla tms.) Tätä yhteyttä tarvitsevat kuitenkin yhdenaikaisesti useat eri toimijat. Koneesta saatetaan siirtää tiedostoja, lukea sähköpostia ja selata www-sivuja yhdenaikaisesti ja on toivottavaa, etteivät näiden toimintojen datat mene keskenään sekaisin. Tämän vuoksi eri yhteydet erotetaan toisistaan porteilla. Kutakin palvelua varten on olemassa yksi tai useampi portti, joita muut palvelut eivät voi käyttää. Mikäli kaksi palvelua käyttää samaa porttia, on vaarana datojen ja sovellusten sekaantuminen. Esimerkiksi Telnet -sovellukset käyttävät perinteisesti porttia 23 ja http (www-sivut) porttia 80. Omissa sovelluksissa on yleensä turvallista käyttää suuria porttinumeroita, vaikkapa 47200, jolloin sekaantumisen vaara on mahdollisimman pieni. Linuxissa (ja yleensä unixeissa) portit, joiden numero on 1024 tai pienempi ovat etuoikeutettuja siinä mielessä, että niitä käyttävää prosessia täytyy ajaa rootin tunnuksilla. Eräs listaus käytössä olevista porteista: http://www.iana.org/assignments/port-numbers

HTTP -protokollassa poikkeava portin numero voidaan ilmaista URL-osoitteessa, esim. http:\\www.hassisenkone.fi:8080.

IP-osoitetta käytetään IP-protokollan tasolla (IP-kehyksessä) identifioimassa tarkoitettu kone verkossa ja porttiosoitetta käytetään TCP-protokollan tasolla (TCP-kehyksessä) identifioimaan haluttu prosessi kyseisessä koneessa. IP-osoitteen ja portin yhdistelmää kutsutaan sokettiosoitteeksi.

Soketti

Soketti (Socket) on eräänlainen verkkokommunikaation päätepiste. Soketti on abstrakti apuväline verkkokommunikaatioon, jonka toiminta ja käyttötapa riippuu käytettävästä ympäristöstä (API, Application Programming Interface). Javassa luokka Socket ( java.net.Socket ) (WWW ) implementoi (asiakas-) soketin toiminnan.

Soketin avulla voidaan lukea ja kirjoittaa tiettyyn määriteltyyn verkkoyhteyteen. Tämän verkkoyhteyden kuvaa ja yksilöi käytettävä IP-osoite ja portti. Soketti kommunikaatio vaatii aina vastinparin, joka käyttää samaa porttia ja sijaitsee valitun ip-osoitteen spesifoimassa kohteessa.

Yleensä sokettipohjaisessa kommunikaatiossa palvelinosan sovelluksessa on käytössä ServerSocket ( WWW )-tyyppinen olio ja asiakaosa käyttää Socket -tyyppistä oliota.

Lisää soketeista

Säikeet (Threads)

Säie voidaan käsittää prosessin osana joka elää omaa elämäänsä. Säikeiden avulla voidaan prosessin prosessoriaikaa jakaa useille yhdenaikaisille tehtäville. Tällöin jokaiselle säikeelle annetaan tietty aikaviipale prosessoriaikaa ja säikeiden suoritus vuorottelee. Tämä vuorottelu on hyvin nopeaa, jolloin syntyy mielikuva useasta yhtä aikaa suorittuvasta tehtävästä. Säikeet eivät ole omia prosessejaan, mutta niitä voi ajatella prosessin sisäisinä prosesseina.

Javassa säikeitä voi ohjelmoida kahdella tavalla. Alla olevassa esimerkissä on luotu oma säieluokka joka perii (extends) luokan java.lang.Thread. (WWW )

public class SatunnaisSaie extends Thread
{
   private String sTeksti = "";
   int iKertaa = 0;
   public SatunnaisSaie(String sTeksti, int iKertaa)
   {
      super();
      this.sTeksti = sTeksti;
      this.iKertaa = iKertaa;
   }
   public void run()
   {
        int i = 0;
        while(i < iKertaa)
        {
            System.out.println(sTeksti);
            i++;
            try
            {
                sleep((long)(Math.random() * 1000));
            }
            catch (InterruptedException e) {}
        }
    }
}

SatunnaisSaie.java

SatunnaisSaie.java:a varten testiluokka, joka vastaa kysymykseen ollako vai eikö olla:

public class SaieTesti
{
   // Vastaus kysymykseen ollako vai eikö olla
   public static void main (String[] args)
   {
      new SatunnaisSaie("Olla", 4).start();
      new SatunnaisSaie("Ei olla", 4).start();
   }
}

SaieTesti.java

Toinen tapa ohjelmoida säie on luoda luokka joka toteuttaa Runnable -rajapinnan.

public class SatunnaisSaie implements Runnable
{
   private String sTeksti = "";
   private int iKertaa = 0;
   private Thread tSaie;
   public SatunnaisSaie(String sTeksti, int iKertaa)
   {
      this.sTeksti = sTeksti;
      this.iKertaa = iKertaa;
   }
   public void start()
   {
      tSaie = new Thread(this, "");
      tSaie.start();
   }
   public void run()
   {
        int i = 0;
        while(i < iKertaa)
        {
            System.out.println(sTeksti);
            i++;
            try
            {
                tSaie.sleep((long)(Math.random() * 1000));
            }
            catch (Exception e)
            {
               System.out.println("VIRHE: " + e.getMessage());
            }
        }
    }
}

SatunnaisSaie.java

Testiluokka on samanlainen kuin edellisessä.

public class SaieTesti
{
   // Vastaus kysymykseen ollako vai eikö olla
   public static void main (String[] args)
   {
      new SatunnaisSaie("Olla", 4).start();
      new SatunnaisSaie("Ei olla", 4).start();
   }
}

SaieTesti.java

Prioriteetit

Jokaisella säikeellä on oma prioriteetti, jolla säikeitä voidaan laittaa tärkeysjärjestykseen. Prioriteetti on kokonaislukuarvo jonka rajat ovat määritelty Thread luokan MIN_PRIORITY ja MAX_PRIORITY -kentät. Säikeen prioriteetti voidaan asettaa setPriority -metodin avulla.(WWW )

Seuraavassa edellisen esimerkin osalta on haluttu varmistua siitä, että vastaukseksi tulee "ei olla" mahdollisimman harvoin.
 

public class SatunnaisSaie extends Thread
{
   private String sTeksti = "";
   private int iKertaa = 0;
   private int iPrioriteetti = MIN_PRIORITY;
   public SatunnaisSaie(String sTeksti, int iKertaa, int iPrioriteetti)
   {
      super();
      this.iPrioriteetti = iPrioriteetti;
      setPriority(iPrioriteetti);
      this.sTeksti = sTeksti;
      this.iKertaa = iKertaa;
   }
   public void run()
   {
        int i = 0;
        while(i < iKertaa)
        {
            System.out.println(sTeksti);
            // Laitetaan vähän viivästystä suoritukseen
            int m = 222;
            for(int k = 10; k < 500000; k++)
               m = ((int)Math.pow(m, k) % 1000);

            i++;
            try
            {
                //sleep((long)(Math.random() * 1000));
                sleep (100);
            }
            catch (Exception e)
            {
               System.out.println("VIRHE: " + e.getMessage());
            }
        }
    }
}
 

SatunnaisSaie.java

public class SaieTesti
{
   // Vastaus kysymykseen ollako vai eikö olla
   public static void main (String[] args)
   {
      new SatunnaisSaie("Olla", 4, java.lang.Thread.MIN_PRIORITY).start();
      new SatunnaisSaie("Ei olla", 4, java.lang.Thread.MAX_PRIORITY).start();
   }
}

SaieTesti.java

SatunnaisSaie2.java
SaieTesti2.java
 

Synkronointi

Säikeiden suorituksessa on joskus tilanteista, joissa jonkin koodin pätkän osalta haluamme vain yhden säikeen olevan suorituksessa kerralla. Tämän voimme ilmaista sanalla synchronized.

Saie.java
SaieTesti.java

Synkronointi voi kohdistua johonkin koodin pätkään tai kokonaiseen metodiin.
 

Säikeen elinikä

Säie pysyy hengissä run() -metodin suorituksen ajan.

http://www.devx.com/upload/free/features/vcdj/2000/05may00/dm0500/dm0500.asp:

   A thread's lifetime continues until the run() method terminates, effectively calling the stop() method. If
   the run method never terminates, then the thread continues to run, even after the program that initiated
   the thread exits. You need to be aware of this so you don't leave orphaned threads and consume
   resources. You should kill a thread performing background operations when the application exits.

http://java.sun.com/products/jdk/1.2/docs/guide/misc/threadPrimitiveDeprecation.html

Verkko-ohjelmoinnissa säikeitä tarvitaan tietovirtojen lukemisessa ja niihin kirjoittamisessa. Sovelluksen käynnissäoloajasta usein vain hyvin pieni osa on aikaa, jolloin jotakin dataa liikkuu verkon yli ja suurin osa ajasta yhteys on käyttämättä. Tällöin on järkevää ettei koko prosessi lukitu odottamaan jotain saapuvaa dataa, vaan data käsitellään sitten kun se on saatavilla.

Tietojenkäsittelytieteen laitos
Kuopion yliopisto
Marko.Hassinen@uku.fi
http://www.cs.uku.fi/~mhassine/VOH/index.html