Osa 1

Web-palvelimen toiminta

Tässä luvussa tarkastellaan web-palvelinten ja selainten toimintaa. Palvelimet noudattavat tyypillisesti asiakas-palvelin -mallia, ja web-palvelimet ja selaimet keskustelevat HTTP-protokollaa noudattaen. Tämän lisäksi tarkastellaan Javan tarjoamia välineitä palvelinyhteyden muodostamiseen sekä palvelimen toteuttamiseen.

Asiakas-palvelin malli

Asiakas-palvelin -mallissa (Client-Server model) asiakkaat käyttävät palvelimen tarjoamia palveluja. Kommunikointi asiakkaan ja palvelimen välillä tapahtuu usein verkon yli siten, että selain ja palvelin sijaitsevat erillisissä fyysisissä sijainneissa (eri tietokoneilla). Palvelin tarjoaa yhden tai useamman palvelun, joita käyttäjä käyttää selaimen kautta.

Käytännössä selain näyttää käyttöliittymän ohjelmiston käyttäjälle. Selaimen käyttäjän ei tarvitse tietää, että kaikki käytetty tieto ei ole hänen koneella. Käyttäjän tehdessä toiminnon selain pyytää tarpeen vaatiessa palvelimelta käyttäjän tarpeeseen liittyvää lisätietoa. Tyypillistä mallille on se, että palvelin tarjoaa vain asiakkaan pyytämät tiedot ja liikkuvan tiedon määrä pidetään vähäisenä.

Asiakas-palvelin -malli mahdollistaa hajautetut ohjelmistot: selainta käyttävät loppukäyttäjät voivat sijaita eri puolilla maapalloa palvelimen sijaitessa tietyssä paikassa.

Yhteyden muodostaminen palvelimelle Java-maailmassa

Ohjelmoijan näkökulmasta porttia voi ajatella tiedostona. Tiedostoon voi kirjoittaa ja siellä olevaa tietoa voi lukea. Kirjoitettava ja luettava tieto ei kuitenkaan toiseen koneeseen yhteyttä ottaessa tule tiedostosta, vaan yhteyden toisessa päässä toimivasta ohjelmasta. Tarkastellaan seuraavaksi yhteyden muodostamista palvelimelle Java-kielellä.

Java-maailmassa yhteys toiselle koneelle muodostetaan Socket-luokan avulla. Kun yhteys on muodostettu, toiselle koneelle lähetettävä viesti kirjoitetaan socketin tarjoamaan OutputStream-rajapintaan. Tämän jälkeen luetaan vastaus socketin tarjoaman InputStream-rajapinnan kautta.

String osoite = "www.helsinki.fi";
int portti = 80;

// muodosta yhteys
Socket socket = new Socket(osoite, portti);

// lähetä viesti palvelimelle
PrintWriter kirjoittaja = new PrintWriter(socket.getOutputStream());
kirjoittaja.println("GET / HTTP/1.1");
kirjoittaja.println("Host: " + osoite);
kirjoittaja.println();
kirjoittaja.flush();

// lue vastaus palvelimelta
Scanner lukija = new Scanner(socket.getInputStream());
while (lukija.hasNextLine()) {
    System.out.println(lukija.nextLine());
}

Yllä oleva ohjelma ottaa yhteyden etsii www.helsinki.fi -osoitteeseen liittyvän palvelimen, ottaa yhteyden palvelimen porttiin 80, ja lähettää palvelimelle seuraavan viestin:

GET / HTTP/1.1
Host: www.helsinki.fi

Tämän jälkeen ohjelma tulostaa palvelimelta saatavan vastauksen.

Lisää verkkoliikenteen käsittelystä löytyy mm. tästä oppaasta.

Loading

Palvelimen toiminta Java-kielellä

Tarkastellaan seuraavaksi palvelimen toimintaa Java-kielellä.

Palvelimen toiminta muistuttaa huomattavasti yllä kuvattua yhteyden muodostamista. Toisin kuin yhteyttä toiseen koneeseen muodostaessa, palvelinta toteutettaessa luodaan ServerSocket-olio, joka kuuntelee tiettyä koneessa olevaa porttia. Kun toinen kone ottaa yhteyden palvelimeen, saadaan käyttöön Socket-olio, joka tarjoaa mahdollisuuden lukemiseen ja kirjoittamiseen.

Web-palvelin lukee tyypillisesti ensin pyynnön, jonka jälkeen pyyntöön kirjoitetaan vastaus. Alla on esimerkki yksinkertaisen palvelimen toiminnasta — palvelin on toiminnassa vain yhden pyynnön ajan.

// odotetaan pyyntöä porttiin 8080
ServerSocket server = new ServerSocket(8080);
Socket socket = server.accept();

// luetaan pyyntö
Scanner lukija = new Scanner(socket.getInputStream());
// ...

// kirjoitetaan vastaus
PrintWriter kirjoittaja = new PrintWriter(socket.getOutputStream());
// ...

// suljetaan resurssit
lukija.close();
kirjoittaja.close();
socket.close();
server.close();

ServerSocket-olion accept-metodi on blokkaava. Tämä tarkoittaa sitä, että accept-metodia kutsuttaessa ohjelman suoritus jää odottamaan kunnes palvelimeen otetaan yhteys. Kun yhteys on muodostettu, accept-metodi palauttaa Socket-olion, jota käytetään palvelimen ja yhteyden ottaneen koneen väliseen kommunikointiin.

Kokeile ylläolevaa ohjelmaa omalla koneellasi. Ohjelma käynnistää palvelimen, joka kuuntelee pyyntöä paikallisen kokeen porttiin 8080. Voit tehdä HTTP-muotoisen pyynnön porttiin 8080 selaimella kirjoittamalla selaimeen osoitteeksi http://localhost:8080.


Palvelin halutaan tyypillisesti toteuttaa niin, että se kuuntelee ja käsittelee pyyntöjä jatkuvasti. Tämä onnistuu toistolauseen avulla.

// luodaan palvelin porttiin 8080
ServerSocket server = new ServerSocket(8080);

while (true) {
    // odotetaan pyyntöä
    Socket socket = server.accept();

    // luetaan pyyntö
    Scanner lukija = new Scanner(socket.getInputStream());
    // ...

    // kirjoitetaan vastaus
    PrintWriter kirjoittaja = new PrintWriter(socket.getOutputStream());
    // ...

    // vapautetaan resurssit
    lukija.close();
    kirjoittaja.close();
    socket.close();
}
Web-palvelimet käsittelevät useampia pyyntöjä lähes samanaikaisesti, sillä palvelinohjelmistot ovat tyypillisesti säikeistettyjä. Tällöin jokainen pyyntö käsitellään erillisessä säikeessä, joka luo pyyntöön vastauksen ja palauttaa sen käyttäjille. Javassa säikeille löytyy oma Thread-luokka. Emme kuitenkaan tällä kurssilla perehdy säikeiden käyttöön sen tarkemmin — mm. tätä varten löytyy kurssi Käyttöjärjestelmät (TKT-20003).

Loading
Loading

Käsittelimme tässä luvussa selaimen ja palvelimen toimintaa sekä toteutimme yksinkertaisen selaimen ja palvelimen. Jatkossa oletamme, että käytössämme on selain (kurssin esimerkeissä oletetaan Google Chrome-selaimen käyttö). Tämän lisäksi jatkossa toteutettavat sovelluksemme hyödyntävät valmiita palvelimia kuten Apache Tomcat ja Eclipse Jetty.

Kun käytössämme on valmiit selaimet ja palvelimet, voimme keskittyä web-sovelluksen logiikan toteuttamiseen.

Pääsit aliluvun loppuun! Jatka tästä seuraavaan osaan:

Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!