Ohjelmoinnin peruskurssien laaja oppimäärä Luento 21: Verkko-ohjelmointia Riku Saikkonen (merkityt ei-laajan kurssin kalvot: Otto Seppälä) 31. 3. 2011
Sisältö 1 Socketit 2 Serialisointi 3 Java-sovelmat
Socketit Socket ja ServerSocket luokat tekevät tiedonsiirrosta verkon yli luonteeltaan samantyyppistä kuin tiedosto-i/o ServerSocket on luokka jolla voidaan tarjota paikka johon jokin toinen ohjelma voi ottaa yhteyden Web-osoite ja porttinumero Socket on luokka jonka kautta voidaan ottaa yhteys johonkin avoimeen serversocketiin Kun yhteys on muodostettu, ohjelma pyytää socketeilta input ja outputstreamit joiden kautta kommunikaatio hoidetaan. Kummassa tahansa päässä yhteyttä voi hyvin olla jokin muu kuin java-ohjelma. Tällä ei ole merkitystä. 09:30 (ei-laajan kurssin kalvo)
Serveri (ServerSocket) Esimerkki (ilman try-catcheja) luodaan luodaan serversocket serversocket ServerSocket ServerSocket serveri serveri new new ServerSocket(12345, ServerSocket(12345, 100); 100); avataan avataan portti portti odottamaan odottamaan yhteydenottoa yhteydenottoa tämä tämä säie säie jatkaa jatkaa vasta vasta kun kun yhteys yhteys on on saatu saatu Socket Socket yhteys yhteys serveri.accept(); serveri.accept(); Kun Kun yhteys yhteys on on avattu, avattu, voidaan voidaan ottaa ottaa tietovirrat tietovirrat InputStream InputStream is is yhteys.getinputstream(); yhteys.getinputstream(); OutputStream OutputStream os os yhteys.getoutputstream(); yhteys.getoutputstream();...... tehdään tehdään mitä mitä tehdään tehdään os.close(); os.close(); is.close(); is.close(); TÄRKEÄÄ! TÄRKEÄÄ! suljetaan suljetaan avattu avattu socket socket yhteys.close(); yhteys.close(); 09:30 (ei-laajan kurssin kalvo)
Client (Socket) Esimerkki (ilman try-catcheja) String String osoite osoite superuniversity.fi ; superuniversity.fi ; luodaan luodaan socket socket ja ja otetaan otetaan yhteys yhteys saman saman tien tien Socket Socket yhteys yhteys new new Socket(osoite, Socket(osoite, 12345); 12345); Kun Kun yhteys yhteys on on avattu, avattu, voidaan voidaan ottaa ottaa tietovirrat tietovirrat InputStream InputStream is is yhteys.getinputstream(); yhteys.getinputstream(); OutputStream OutputStream os os yhteys.getoutputstream(); yhteys.getoutputstream();...... tehdään tehdään mitä mitä tehdään tehdään TÄRKEÄÄ! TÄRKEÄÄ! suljetaan suljetaan kaikki kaikki os.close(); os.close(); is.close(); is.close(); yhteys.close(); yhteys.close(); 09:30 (ei-laajan kurssin kalvo)
Millaisia verkkoyhteyksiä on? edellinen esimerkki käytti Internetin TCP/IP-protokollaa se on tietovirtapohjainen (stream): avattu yhteys näyttää tiedostolta, eli siitä luetaan ja kirjoitetaan tavujonoja oikeasti data liikkuu paketeissa (usein 1500 tavua), joita voi kadota tai joiden järjestys voi muuttua matkalla TCP-protokolla järjestää paketit ja pyytää tarvittaessa uudelleenlähetyksiä (ei näy ohjelmalle) toinen, harvinaisempi vaihtoehto on UDP/IP pakettipohjainen (datagram): lähetetään ja vastaanotetaan tavujonojen sijaan kokonaisia paketteja pakettien lähetykseen ja vastaanottoon on useimmissa kielissä omat funktionsa ohjelma saa paketit kokonaisina ja sitä mukaa kun niitä tulee siis paketteja voi jäädä tulematta tai tulla eri järjestyksessä käytetään esim. reaaliaikaista ääni- ja videodataa siirrettäessä muutama muukin tämän tason protokolla on
Portit ja yhteydet yhteyden muodostaminen TCP:ssä ja UDP:ssä: 1 Palvelin kuuntelee (listen) oman IP-osoitteensa tiettyä porttia, esim. 12345 2 Asiakas ottaa yhteyden (connect) tähän IP-osoitteeseen ja porttiin 3 Palvelin hyväksyy (accept) tämän yhteydenottopyynnön (voi hyväksyä useammankin pyynnön: niistä tulee erilliset yhteydet) yhteydenmuodostuksen jälkeen yhteys on symmetrinen: kumpi tahansa voi lähettää dataa toiselle tai sulkea yhteyden portti (port) on numero väliltä 165535 useimmiten kuunnellaan tietyssä portissa, joka kuuluu jollekin palvelulle (esim. useimmat HTTP-palvelimet portissa 80) avattu yhteys (connection) on nelikko, jossa on kummankin pään IP-osoite ja kummankin pään valitsema portti asiakas valitsee itselleen yleensä satunnaisen portin porttiparilla erotetaan samojen koneiden väliset yhteydet toisistaan esim. WWW-palvelimeen voisi olla yhtäaikaiset yhteydet {12.12.12.12, 1025, 1.2.3.4, 80 ja {12.12.12.12, 1524, 1.2.3.4, 80
Nimipalvelin TCP/IP ja UDP/IP toimivat IP-osoitteilla (numeroita) koneiden tai palveluiden nimet muutetaan IP-osoitteiksi kysymällä niitä nimipalvelimelta (domain name server) Internetissä on hierarkia nimipalvelimia, joilta nimiä kysytään esim. www.aalto.fi: juurinimipalvelin (root server) tietää fi-nimipalvelimen osoitteen, joka tietää aalto.fi-nimipalvelimen osoitteen, joka tietää www.aalto.fi-koneen osoitteen tämä on harvoja osia Internetistä, joka on hallinnollisesti keskitettyä esim. fi-nimipalvelin päättää kaikista.fi-päätteisistä osoitteista (vaikka se yleensä delegoi päätökset alemman tason palvelimille) kuormitussyistä ylempien tasojen nimipalvelimia on monta samanlaista (esim. juurinimipalvelimia kymmeniä) käytännössä lähiverkossa on melkein aina apunimipalvelin, jolta voi kysellä nimiä ja joka kysyy niitä eteenpäin muilta ja tallettaa kyselyiden tulokset välimuistiinsa, joten samojen koneiden nimiä ei tarvitse ei tarvitse koko ajan kysellä periltä asti
Nimipalvelin ohjelmoijan näkökulmasta nimipalvelin ja siihen liittyvät protokollat piilotetaan enimmäkseen ohjelmoijalta: joko kirjastoissa on kutsu, jolla voi tehdä nimipalvelukyselyn tai yhteydenmuodostuskomennolle voi antaa joko IP-osoitteen tai nimen käytännön rajapintaongelma: useimmiten nimipalvelukysely jää odottamaan vastausta (blocking) siis ohjelma pysähtyy siksi aikaa kirjastorajapinnat, joissa ohjelma voisi tehdä jotain odotuksen aikana, ovat harvinaisia jos tämä on ongelma, nimipalvelukyselyt voi tehdä esim. eri säikeessä jotkin ohjelmat, esim. WWW-selaimet, käynnistävät oman aliprosessin nimipalvelukyselyitä varten (se pitää lisäksi tulokset tallessa muistissaan)
TCP:n päällä olevat protokollat TCP/IP siis tarjoaa yhteyden, jossa voi siirtää vapaamuotoista dataa (tavuja tai merkkejä) edestakaisin sen päälle on Internetissä tehty paljon sovellustason protokollia esim. HTTP, jossa asiakas pyytää webbisivua (avaa palvelimeen yhteyden ja lähettää URLin tietyssä muodossa) ja palvelin vastaa sivun sisällöllä tai NNTP, jolla luetaan ja kirjoitetaan nyyssejä yhden rivin pituisilla ASCII-komennoilla nämä on dokumentoitu ns. RFC-dokumenteissa, joista osa on valittu Internet-standardeiksi useimmat ovat asiakaspalvelin-tyyppisiä joskus käytetään myös peer-to-peer-palveluita, joissa molemmat päät ovat samanarvoisia yhteydenmuodostus on näissä monimutkaisempaa, varsinkin palomuurien ja osoitemuunnosten (NAT) läpi käytetään esim. joissain verkkopeleissä ja IP-puheluissa
Paikalliset socketit periaatteessa socketit eivät ole vain Internet-yhteyksiä varten varsinkin Unix-koneissa niitä käytetään myös koneen sisäiseen kommunikointiin ohjelmien välillä Unix domain socket on kuten TCP, mutta paikallinen: portin tilalla on (yleensä) tiedoston nimi, jossa kuunnellaan ja johon otetaan yhteyttä tiedosto on tyypiltään socket ( s ls-komennossa) muodostettu yhteys toimii kuten TCP, mutta koneen sisällä lisäksi yhteydestä saa selville sen toisen pään käyttäjänimen (sillä tai tiedoston oikeuksilla voi rajoittaa sockettiin pääsyä) etuna on varsinkin viimeinen: UDP- ja TCP-yhteyksiin pääsee ainakin koko sama kone, tavallisesti myös koneen ulkopuolelta UDP- ja TCP-yhteydet voi rajoittaa samasta koneesta (mutta muiltakin käyttäjiltä) tuleviksi vaihtamalla kuunneltava osoite (listen address, bind address) localhost-osoitteeksi 127.0.0.1
Vinkkejä kokeiluun komentoriviohjelmilla nc ja socat voi avata yksittäisiä verkkoyhteyksiä kummin päin tahansa ohjelmilla host ja dig voi tehdä nimipalvelukyselyjä omalla koneella (ylläpito-oikeuksilla) esim. ohjelmalla tcpdump voi seurata koneen lähettämiä ja vastaanottamia paketteja varoitus: kuuntelevaan TCP- tai UDP-sockettiin pääsee koko maailma! paitsi jos palomuuri sattuu estämään (silloinkin pääsee sama kone ja yleensä koko lähiverkko) kokeilut kannattaa tehdä esim. kotikoneella, joka on irti verkosta varsinkin kuunteleva ja pitkään päällä oleva palvelin on tietoturvariski (oleta, että verkosta tuleva syöte on vihamielistä!) Aalto-koneilla Työaseman tai palvelimen ulkopuolelle näkyvien palveluprosessien pystyttäminen edellyttää tietojärjestelmän omistajan lupaa. (käyttöpolitiikka 3. 6. 2010) lisää verkkoyhteyksistä esim. kurssin T-110.2100 luentokalvoilta
Sisältö 1 Socketit 2 Serialisointi 3 Java-sovelmat
Yleisiä tapoja datan siirtoon useimmat ohjelmat tallettavat ja lataavat dataa tiedostoista tai esim. verkon yli toiselta ohjelmalta esim. lautapeli voisi tallentaa pelin (laudan sisällön) tai verkkopelinä toimiessaan lähettää sen toiselle pelaajalle miten tällainen data kannattaa esittää? itse keksityllä binääri- tai tekstitiedostolla? jollain puolivalmiilla formaatilla kuten XML:nä? jonkin muun ohjelman ennestään käyttämällä formaatilla? esim. omia binääriformaatteja on helppo tuottaa joistain ohjelmointikielistä (muistin sisältö levylle), mutta vaikea käsitellä muilla kielillä tai eri versioilla seuraavilla kalvoilla on muutama yleinen puolivalmis formaatti
Datan serialisointi serialisointi (serialization, marshalling) tarkoittaa ohjelmassa olevan datan muuttamista merkkijonoksi ja takaisin erityisesti ohjelmointikielen tietorakenteiden automaattista tallentamista ja lataamista esim. Schemessä read ja write toteuttavat Scheme-datan serialisoinnin Javassa on valmis kirjasto olioiden serialisointiin Javan omaan binääriformaattiin kaikkea ei voi serialisoida (esim. funktioita) usein serialisointi tehdään kuitenkin käytännössä osittain käsin esimerkiksi jotta ohjelman eri versiot toimisivat ristiin, vaikka olioiden rakenne muuttuu
Sulkulausekkeet Esimerkki datasta (osm ((version "0.6") (generator "CGImap 0.0.2")) (bounds 60.1711000 24.8552600 60.1760400 24.8672200) (way 26026161 (user "alv") (uid 4660) (visible #t) (version 2) (changeset 139696) (timestamp "2008-08-08T08:00:57Z") (nodes 284132199 284132200 286097549) (tags ("highway" "footway") ("surface" "unpaved")))) Schemen read- ja write-primitiivit käyttävät Scheme-koodin näköistä sulkulauseke-esitysmuotoa esitysmuoto on nimeltään S-expression (symbolic expression) sitä on kohtalaisen helppo kirjoittaa ja lukea automaattisesti ilmankin ohjelmointikielen tukea
JSON (JavaScript Object Notation) Esimerkki datasta { "osm": { "version": "0.6", "generator": "CGImap 0.0.2", "bounds": { "minlat": 60.1711000, "minlon": 24.8552600, "maxlat": 60.1760400, "maxlon": 24.8672200, "ways": [ { "id": 26026161, "user": "alv", "uid": 4660, "visible": true, "version": 2, "changeset": 139696, "timestamp": "2008-08-08T08:00:57Z", "nodes": [ { "ref": 284132199, { "ref": 284132200, { "ref": 286097549 ], "tags": [ { "k": "highway", "v": "footway", { "k": "surface", "v": "unpaved" ] ] JSON-formaatti on tehty JavaScript-kielen pohjalta periaatteessa se on koodia (olion ja/tai taulukon alustus), jonka voisi suoraan antaa JavaScript-tulkille käytännössä sitä useimmiten luetaan kirjaston avulla käytetään varsinkin webbisovelluksissa selaimessa pyörivän JavaScript-koodin ja palvelimen väliseen tiedonsiirtoon
XML Esimerkki datasta (pala OpenStreetMap-dataa) <?xml version"1.0" encoding"utf-8"?> <osm version"0.6" generator"cgimap 0.0.2"> <bounds minlat"60.1711000" minlon"24.8552600" maxlat"60.1760400" maxlon"24.8672200"/> <way id"26026161" user"alv" uid"4660" visible"true" version"2" changeset"139696" timestamp"2008-08-08t08:00:57z"> <nd ref"284132199"/> <nd ref"284132200"/> <nd ref"286097549"/> <tag k"highway" v"footway"/> <tag k"surface" v"unpaved"/> </way> </osm> XML-esitysmuotoon kuuluu yleensä skeema (schema), joka määrittelee, mitä alikohtia missäkin saa olla ja valmiita työkaluja, jotka tarkistavat skeemanmukaisuuden, editoivat XML:ää, hakevat XML:stä osia tietyillä hakuehdoilla, konvertoivat XML:ää muunlaisiin formaatteihin, jne. XML:n käsittelyyn on kirjastoja useimmissa ohjelmointikielissä
Itse tehdyt formaatit hyvin usein ohjelmissa käytetään edellä lueteltujen formaattien sijaan omia itse suunniteltuja formaatteja etuja: joustavampia ja tilanteeseen sopivampia, joskus yksinkertaisempia ymmärtää, usein helpompia kirjoittaa käsin haittoja: itse tehty jäsennin ei yleensä ole yhtä monipuolinen; omaa formaattia käsitteleviä muita työkaluja ei ole valmiina etu ja haitta: ei tarvitse käyttää valmiita kirjastoja se, että jokin data esitetään sulkulausekkeina, JSONina tai XML:nä ei useinkaan kerro vielä kovin paljon pitää vielä määritellä, miten niitä käytetään nämä valmiit formaatit helpottavat suunnittelua, mutta eivät tee sitä ohjelmoijan puolesta usein näiden valmiiden formaattien sisälle (esim. tiettyihin merkkijonoihin) määritellään vielä itse jokin oma formaatti
Sisältö 1 Socketit 2 Serialisointi 3 Java-sovelmat
Sovelma - Applet Sovelma on Java-ohjelma joka voidaan upottaa htmlsivulle. Sovelman pääikkuna, JApplet toimii pitkälti samoin kuin esim JFrame, mutta : Ohjelman pääikkuna on JApplet-olio, jonka selain luo (konstruktoria ei kutsuta) Konstruktorikutsun / main-metodin sijaan JAppletissa on metodi public void init(). Tässä metodissa tehdään konstruktoria vastaavat muuttujien alustukset (usein vain tämä metodi toteutetaan) Kun sovelma on täysin latautunut kutsutaan automaattisesti public void start(). Jos sovelma halutaan pysäyttää kutsutaan automaattisesti metodia public void stop(). Juuri ennen sovelman sulkemista kutsutaan metodia destroy(), jossa 09:30 voi vielä hoitaa resurssien vapautusta tai tiedon laittamista talteen (ei-laajan kurssin kalvo)
Esimerkki Seuraavalla kalvolla on esitetty esimerkki yksinkertaisesta sovelmasta. Sovelman suorittaminen lähtee liikkeelle init()-metodista. Koodi joka alustaa sovelman sisältämän käyttöliittymän säynnistetään tämän metodin toimesta. Käytännössä kaiken Swing-käyttöliittymää modifioivan koodin tulisi toimia tapahtumankuuntelusäikeen sisältä. Seuraavalla sivulla esitetty koodi tekeekin asian oikeammin kuin aiemmin näkemämme yksinkertaistetut esimerkit....näyttää monimutkaiselta, mutta on käytännössä sovellettavissa sellaisenaan, joten swingin tai säikeiden syvällisempää osaamista ei oikeasti tarvita. 09:30 (ei-laajan kurssin kalvo)
Seuraavat rivit voivat näyttää raskailta ja monimutkaisilta. Niiden tehtävä on suorittaa graafisen käyttöliittymän luominen tapahtumankuuntelusäi keessä. Tämä on Swingin rakenteesta seuraava vaatimus. Tässä luodaan Runnableolio jonka run-metodi toimii aloitus-pisteenä käyttöliittymän luonnille Ja tässä käyttämälllä luokan SwingUtilities metodia invokeandwait suoritetaan ylläesitetyn Runnable-olion run metodi tapahtumankuuntelusäikeessä HUOM: (run-metodia ei siis kutsuta suoraan) import java.awt.*; import java.awt.*; import java.awt.event.*; import java.awt.event.*; import javax.swing.*; import javax.swing.*; public class AppletFromFrame extends JApplet { public class AppletFromFrame extends JApplet { private JButton button; private JButton button; private JLabel label; private JLabel label; public void init() { public void init() { Runnable guicreator new Runnable() { Runnable guicreator new Runnable() { public void run() { public void run() { creategui(); creategui(); ; ; try { try { javax.swing.swingutilities.invokeandwait(guicreator); javax.swing.swingutilities.invokeandwait(guicreator); catch (InterruptedException e) { catch (InterruptedException e) { e.printstacktrace(); e.printstacktrace(); catch (java.lang.reflect.invocationtargetexception e) { catch (java.lang.reflect.invocationtargetexception e) { e.printstacktrace(); e.printstacktrace(); /* /* * Allaoleva metodi siis suoritetaan ylläolevan koodin avulla * Allaoleva metodi siis suoritetaan ylläolevan koodin avulla * tapahtumankuuntelusäikeessä. * tapahtumankuuntelusäikeessä. */ */ private void creategui() { private void creategui() {...jatkuu seuraavalla kalvolla...jatkuu seuraavalla kalvolla init-metodia kutsutaan suoraan selaimen toimesta, ja tällöin Applet-oliokin on jo luotu, joten periaatteessa init on appletin "käynnistysmetodi". 09:30 (ei-laajan kurssin kalvo)
/* /* * Tämä metodi siis suoritetaan edellisen kalvon koodin * Tämä metodi siis suoritetaan edellisen kalvon koodin * avulla tapahtumankuuntelusäikeessä. * avulla tapahtumankuuntelusäikeessä. * * * Huomaa että koodi on aivan samanlaista kuin tavallisen * Huomaa että koodi on aivan samanlaista kuin tavallisen * käyttöliittymän luontikoodi * käyttöliittymän luontikoodi */ */ private void creategui() { private void creategui() { button new JButton("Press Me!"); button new JButton("Press Me!"); label new JLabel("Not pressed"); label new JLabel("Not pressed"); button.addactionlistener(new ActionListener() { button.addactionlistener(new ActionListener() { public void actionperformed(actionevent arg0) { public void actionperformed(actionevent arg0) { label.settext("pressed"); label.settext("pressed"); ); ); Container contents this.getcontentpane(); Container contents this.getcontentpane(); contents.setlayout(new GridLayout(1,2)); contents.setlayout(new GridLayout(1,2)); contents.add(button); contents.add(button); contents.add(label); contents.add(label); private static final long serialversionuid -4214220844287255698L; private static final long serialversionuid -4214220844287255698L; 09:30 (ei-laajan kurssin kalvo)
Sovelma Kuinka sovelma lisätään www-sivulle? Sovelma koostuu yleensä monesta luokasta, joten sovelmaan kuuluvat käännetyt luokkatiedostot + mahdolliset data-tiedostot sijoitetaan yleensä JAR-pakettiin www-sivun koodiin lisätään applet-tagi Kertoo appletin sisältävän jar-tiedoston nimen Kertoo appletin pääluokan nimen Kertoo appletin koon Mahdollistaa parametrien antamisen appletille <APPLET <APPLET archive archive Myapplet.jar Myapplet.jar code code Example.class Example.class width width 200 200 height height 200> 200> Tämä Tämä teksti teksti näkyy näkyy jos jos applettia applettia ei ei voida voida ladata ladata </APPLET> </APPLET> 09:30 (ei-laajan kurssin kalvo)
Hiekkalaatikko Sovelma suorittaa toimintansa ns. hiekkalaatikossa Tämä estää Tiedostojen lukemisen levyltä Verkkoyhteyksien ottamisen muille koneille, kuin miltä sovelma ladattiin Näiden muutosten vuoksi mm. ulkoisten tiedostojen lataaminen täytyy toteuttaa hieman eri tavalla kuin normaaliohjelmassa a)ladataan tiedostot verkko-osoitteen (URL) kautta palvelimelta, jolta applet ladattiin. b)ladataan tiedostot samasta JAR-paketista, jossa javan classtiedostotkin ovat 09:30 (ei-laajan kurssin kalvo)
Tiedostojen lataaminen JAR-paketista Eräs yksinkertainen tapa ladata tiedostoja jar-paketista on käyttää luokkalataajaa tiedostojen avaamiseen Luokkalataaja on mekanismi, joka hoitaa luokkien dynaamisen noutamisen levyltä, verkosta jne. ohjelman ajettavaksi Sama luokkalataaja joka latasi jonkin sovelmasi luokan levyltä, pystyy lukemaan myös muita tiedostoja tästä paikasta Allaoleva koodi käyttää tätä menetelmää try{ try{ InputStream InputStream is is this.getclass().getresourceasstream( tiedosto.txt ); this.getclass().getresourceasstream( tiedosto.txt ); tehdään tehdään jotain jotain catch catch ( ( IOException IOException e e ) ) { { reagoidaan reagoidaan jollain jollain tavalla tavalla 09:30 (ei-laajan kurssin kalvo)
Tiedostojen lataaminen verkosta Appletista on yksinkertaista ladata tiedostoja (erityisesti kuvia) samalta palvelimelta jolta se on ladattu ImageIcon-luokan konstruktorit toimivat verkossa identtisesti verrattuna ohjelmaan joka lataa tiedostoja levyltä Image-olioita voidaan ladata Appletin metodilla getimage() Tällöin tarvitaan kuitenkin appletin www-osoite, joka saadaan metodilla getcodebase() ImageIcon ImageIcon kuva1 kuva1 new new ImageIcon( test.jpg ); ImageIcon( test.jpg ); Image Image kuva2 kuva2 getimage(getcodebase(), getimage(getcodebase(), test2.jpg ); test2.jpg ); 09:30 (ei-laajan kurssin kalvo)
Sovelmien allekirjoittaminen Sovelman voi tarvittaessa allekirjoittaa Tapahtuu ohjelmalla appletsigner Allekirjoitettu sovelma saa täydet käyttöoikeudet koneeseen jolla sitä ajetaan Saa lukea ja kirjoittaa käyttäjän oikeuksilla Saa ottaa verkkoyhteyksiä kaikkialle jne. Suhtaudu varauksella allekirjoitettuihin sovelmiin jos et tunne tahoa joka tarjoaa allekirjoitetun sovelman 09:30 (ei-laajan kurssin kalvo)