Võrguprogrammeerimise elemente

Internet -  teenused, protokollid, pordid...

Sageli on Interneti teenused korraldatud nn. klient/server mudelit aluseks võttes. Teenuse saamiseks esitab klient tellimuse, millele vastuseks server  võib algatada protsessi tellimuse täitmiseks. Põhimõtteliselt võib sama tellimuse täitmisse olla kaasatud mitu serverit. Kommunikatsiooni aktiivne pool on klient (vahel kasutatakse seda määratlust mõiste "klient" defineerimiseks).

Tuleb teha  vahet teenusel (näiteks failide ülekanne: FTP - file transfer protocol), seda teenust realiseerival tarkvaral (ftp-server ühel pool ning ftp-klient teisel pool) ja protokollil, mis teeb võimalikuks nende programmide vahelise suhtlemise (ftp-protokoll). Sõltuvalt kontekstist tuleb siis selgeks teha, millest on jutt, sest üldnimi  tähistab neid kõiki.

Server-programm (spetsiifilisemas sõnavaras deemon) on pidevalt valvel ja kuulab, kas tema poole soovib pöörduda mõni klient, olles üldjuhul võimeline teenindama kliente paralleelselt (Java seisukohalt vaadates - server algatab uue kliendi jaoks uue lõime).

Interneti klient/server programmid suhtlevad nn. portide kaudu.
Vastavalt protokolli tüübile jagatakse porte TCP- ja UDP-portideks. Näiteks paljudes Unixi versioonides on üks Interneti deemon - inetd, mis kuulab TCP-porte ja organiseerib konkreetsete serverprogrammide täitmist. TCP - transmission control protocol - on Interneti protokoll (mis OSI mudeli järgi kuulub transpordikihti), mis tagab pideva ühenduse kahe võrgusõlme vahel.

Iga teenusega on seotud kindel protokoll infovahetuseks kliendi ja serveri vahel. Konkreetsed masinad, millel klient ja server  töötavad, võivad olla väga erinevad - see ei tohiks olla oluline (vahel on klient ja server ka samas masinas).

Protokollideks on näiteks:
telnet - kaugterminaaliga suhtlemise protokoll
ftp, ftp-data - failide ülekande protokollid (file transfer protocol)
smtp - kirjavahetuse protokoll (simple mail transfer protocol)
nntp - võrguuudiste edastamise protokoll (network news transfer protocol)
http - hüperteksti edastamise protokoll (hypertext transfer protocol)
finger - protokoll lühiinformatsiooni edastamiseks kasutaja või süsteemi kohta
rpc - hajussüsteemide tööks vajalik kaugprotseduuri väljakutse protokoll (remote procedure call)
snmp - võrgu teeninduseks vajalik protokoll (simple network management protocol)
ntp - ajateenistusprotokoll (network time protocol)
nfs - protokoll hajusa välismäluga töötamiseks (network file system)
....

Joonis
Samas arvutis võib korraga olla käimas mitmeid rakendusi, nii kliente kui ka servereid. Et aru saada, milline pakett millise rakenduse juurde kuulub, tuleb korraldada nende sorteerimine nn. portide kaupa. Porte iseloomustatakse numbrite abil (täisarv vahemikus 0 kuni 65535), seejuures numbrid 0 kuni 1023 on reserveeritud kindlate rakenduste jaoks (ingl.k. well known ports)

Teenusega serveri poolt on seotud  konkreetne pordi number (näiteks e-post - 25, telnet - 23 jne.), kuid kliendipoolne pordi number määratakse dünaamiliselt, et tagada ühenduse ühene identifitseerimine. Näiteks saab samade arvutite vahel korraga saata mitut kirja. Selleks, et erinevatesse kirjadesse kuuluvad paketid segi ei läheks, peab iga kirja jaoks olema eraldi ühendus, s.t. erinev komplekt pordinumbreid.

Kokkuvõttes iseloomustab iga seanssi 4 parameetrit: kummagi osapoole IP-aadress ning pordi number, mille kaudu suhtlemine toimub. Kasutajale on oluline teada just serveripoolseid (standardseid) pordinumbreid, et ühendust algatada juhtumil, kui kasutatakse mittetraditsioonilisi teenuseid. Pordinumber on mittekohustuslik osa URL (uniform resource locator) koosseisus.

Tehniliselt (protokolle aluseks võttes) võib eristada nn. UDP-porte (user datagram protocol) (näit. 69 - TFTP trivial ftp, 123 - NTP network time protocol,  161 - SNMP simple network management protocol, ....), mis tagavad kohalejõudmisgarantiideta kiire paketiedastuse ja TCP-porte (transmission control protocol) (näit. 20 - FTP-DATA, 21 - FTP, 23 - TELNET, 25 - SMTP, 79 - FINGER, 119 -NNTP ....), mis tagavad pideva ühenduse sõlmede vahel. TCP-portide ja UDP-portide numbrid ei ole omavahel mitte kuidagi seotud!

DNS - Domain Name Service on teenus IP-aadresside seostamiseks nimedega. Iga nimega ei pruugi olla seotud IP aadressi, sest näiteks e-post liigub ka muudes (mitte-TCP/IP) võrkudes. Samuti ei pruugi igale IP-aadressi omavale võrguseadmele panna nime.
Nimed Internetis moodustavad loogilise hierarhia - üldisemad nimekomponendid paiknevad seejuures tagapool. Juurtasemel: .ee .fi .se .de .fr .uk .no  jne. riikide kaupa; .com .edu .gov .mil .net .org  tegevusala järgi (peamiselt USA).
Nimedoomen (näit.  .ut.ee ) ühendab teatud hulka masinaid loogiliselt ning on üksuseks administreerimisel. Masinanimi on esimene komponent Internet-nimes (näit. romulus.cs.ut.ee).
Alati on soovitav kasutada täielikku Interneti-nime (eriti kirjade saatmisel).
IP aadress on seotud füüsilise võrguga ning selle jagunemisega alamvõrkudeks. IP aadressi teadmisel saab masina poole pöörduda nimeserveri vahenduseta (praktikas on hea teada oluliste masinate IP-aadresse, kindlasti enda masina  oma). Masinanimi on lokaalse tähendusega ning tihedalt seotud operatsioonisüsteemiga. Tavaliselt saab ühes lokaalvõrgus olevate masinate Interneti-nime asemel kasutada masinanime (näit.  rlogin brutus). Võib korraldada nii, et samas doomenis olevate masinate poole pöördumisel tohib doomeni nime osa ära jätta.
Nimeserver lubab nn. kanoonilise nime kõrvale luua ka sünonüüme (näit. ftp.ut.ee, archie.funet.fi). Heaks kombeks ongi kasutada teenuste identifitseerimiseks teenuse nime esimese nimekomponendina. Huvitab ju kasutajat ennekõike juurdepääs teenusele (näit. news.ut.ee), mitte antud hetkel seda teenust pakkuva masina kanooniline nimi (näit. kadri.ut.ee).
Nimeserveris on kirjas IP aadress, masina kanooniline nimi, andmed riistvara ja OS kohta, andmed kirjade haldamise kohta jne. Tuleks teada oma lähimat nimeserverit ja lisaks mõnda
tagavaraserverit. Internetiühenduse konfigureerimisel ongi tüüpiliselt tarvis teada oma masina IP-aadressi, võrgumaski, nimeserverit ja marsruuti, kuhu väljuvad paketid suunata.
NAT - Network Address Translation, tulemüürid.

Paketi java.net mõned vahendid

Internet URL tasemel

InetAddress
URL
URLConnection

TCP pistikliides

Socket
ServerSocket
SocketException ( <- IOException <- Exception )

UDP pistikliides

DatagramPacket
DatagramSocket

Ühenduseta protokollid - UDP, datagrammide saatmine

Näide - server

/*
 * Fail Dserver.java
 */

import java.net.*;
import java.io.*;
import java.util.Date;

public class Dserver {

   public static void main (String [] parameetrid) {

      int pordinumber;                  // UDP-pordi number
      if (parameetrid.length == 0)
         pordinumber = 5678;
      else pordinumber = Integer.parseInt (parameetrid [0]);

      try {
         DatagramSocket dpistik = new DatagramSocket(pordinumber);
         // analoog serverpistikuga -- istume ja kuulame

         while (true) { // lopmatu tsu"kkel, ctrl-c abil va"lja
            byte [] andmed = new byte [1024];
            DatagramPacket pakett = new DatagramPacket
               (
andmed, andmed.length);
            dpistik.receive (pakett);
            String teade = new String
               (pakett.getData(), 0, pakett.getLength());
            InetAddress aadress = pakett.getAddress();
            System.out.println ("\nSaabus " + aadress.toString() + 
               " " + new Date().toString() + ":\n" + teade);
         }
      }
      catch (IOException e) {
         System.out.println (e);
      }
   } // main lopp

} // Dserver lopp

Klient (rakend)

/**
 * Fail Dklient.java
 */

import java.net.*;
import java.io.*;
import java.applet.Applet;
import java.util.Date;
import java.awt.Graphics;

public class Dklient extends Applet { // kliendipool on rakend

   InetAddress aadress;    // serveri aadress
   int pordinumber;        // suhtlemiseks kasutatav UDP-port

   public void init() { // Applet-klassi meetodi u"lekatmine
      try {
         aadress=InetAddress.getByName(getCodeBase().getHost());
         if (getParameter ("port") == null) 
            pordinumber = 5678; // suvaliselt valitud va"a"rtus
         else pordinumber =Integer.parseInt(getParameter("port"));
            // pordinumber tuleb veebilehelt parameetrina
      }
      catch (UnknownHostException e) {
         // to"o"delda
      }
   }

   void saada (String teade) {
      try {
         byte [] andmed = teade.getBytes();
         DatagramPacket pakett = new DatagramPacket
            (andmed, andmed.length, aadress, pordinumber);
         DatagramSocket dpistik = new DatagramSocket();
         dpistik.send (pakett);
         dpistik.close();
      }
      catch (IOException e) {
         // to"o"delda
      }
   }

   public void start() { // Applet-klassi meetodi u"lekatmine
      String s = "  " + new Date().toString() + " " +
         System.getProperty ("os.name") + " " +
         System.getProperty ("os.version") + " " +
         System.getProperty ("os.arch") + "\n  " +
         System.getProperty ("java.vendor");
      saada (s);
   }

   public void stop() { // Applet-klassi meetodi u"lekatmine
      String s = "  " + new Date().toString() + " lahkus lehelt";
      saada (s);
   }

   public void paint (Graphics ekraan) { // Applet-klassi meetod
      ekraan.drawString ("Spioon" , 50, 25);
   }

} // Dklient lopp

Kliendipoolne veebileht

<!-- Fail Dklient.html -->
<!-- autor: J.Poial -->

<HTML>
  <HEAD>
    <TITLE>Klient</TITLE>
  </HEAD>
  <BODY>
    <APPLET code="Dklient.class" width=300 height=100>
      <param name="port" value="5678">
    </APPLET>
  </BODY>
</HTML>

Ühendusega protokollid - TCP, voogude töötlemine

Pistikliidesega klient (tellimus standardsisendist)

/**
 * Fail DirKlient.java
 */

import java.net.*;
import java.io.*;

public class DirKlient {

   public static void main(String[] parameetrid) {
      String serveriNimi = "localhost";    // server
      int pordinumber = 7654;              // TCP-port
      if (parameetrid.length == 1)
         pordinumber = Integer.parseInt (parameetrid [0]);
      else if (parameetrid.length > 1) {
         serveriNimi = parameetrid [0];
         pordinumber = Integer.parseInt (parameetrid [1]);
      }
      try {
         Socket pistik = new Socket (serveriNimi, pordinumber);
         InputStream sisse = pistik.getInputStream();
         BufferedReader svoog = new BufferedReader
            (new InputStreamReader (sisse));
         OutputStream valja = pistik.getOutputStream();
         PrintWriter vvoog = new PrintWriter
            (new OutputStreamWriter (valja), true);
         BufferedReader klav = new BufferedReader
            (new InputStreamReader (System.in));
         System.out.print ("Anna tellimus: ");
         String tellimus = klav.readLine();
         System.out.println ("Saadan tellimuse: host = "
            + pistik.getInetAddress() + " , port = "
            + pistik.getPort() + "\n localhost = "
            + pistik.getLocalAddress() + " , localport = "
            + pistik.getLocalPort());
         vvoog.println (tellimus);
         System.out.println ("Sain vastuseks:");
         String rida;
         while ((rida = svoog.readLine()) != null)
            System.out.println (rida);
         pistik.close();
      }
      catch (IOException e) {
         System.out.println ("S/V viga: " + e);
      }
   } // main lopp

} // DirKlient lopp
 

Server (vastuseks kataloogi sisu)

/**
 * Fail DirServer.java
 */

import java.io.*;
import java.net.*;

public class DirServer {

   public static void main (String [] parameetrid) {
      int pordinumber = 7654;  // TCP-port
      if (parameetrid.length > 0)
         pordinumber = Integer.parseInt (parameetrid [0]);
      try {
         ServerSocket kuulaja = new ServerSocket (pordinumber);
         while (true) {
            new DirLoim (kuulaja.accept()); //argument on Socket
         }
      }
      catch (IOException e) {
         System.out.println ("S/V viga: " + e);
      }
   } // main lopp

} // DirServer lopp

class DirLoim extends Thread {

   Socket pistik; // igal lo~imel oma pistik

   DirLoim (Socket p) {
      pistik = p;
      setPriority (NORM_PRIORITY - 1);
      // kuulamine on pisut tähtsam kui töötlemine
      start();
   }

   public void run() { // katame üle
      try {
         InputStream sisse = pistik.getInputStream();
         BufferedReader svoog = new BufferedReader
            (new InputStreamReader (sisse));
         OutputStream valja = pistik.getOutputStream();
         PrintWriter vvoog = new PrintWriter
            (new OutputStreamWriter (valja), true);
         String rida;
         if ((rida = svoog.readLine()) != null) {
            System.out.println ("Pordi " + pistik.getPort()
               + " kaudu saabus tellimus:\n" + rida);
            String vastus = annaVastus (rida);
            vvoog.println (vastus);
            System.out.println ("Pordi " + pistik.getPort()
               + " kaudu saadeti vastus:\n" + vastus);
         }
         pistik.close();
      }
      catch (IOException e) {
         System.out.println ("S/V viga: " + e);
      }
   } // run lopp

   static String annaVastus (String tellimus) {
      File f = new File (tellimus);
      if (!f.exists() || !f.canRead())
         return ("Ei saanud aru: " + tellimus);
      if (f.isDirectory()) {
         String [] nimekiri = f.list();
         StringBuffer vastus = new StringBuffer();
         for (int i=0; i<nimekiri.length; i++) {
            vastus.append (nimekiri [i]);
            vastus.append (" ");
         }
         return vastus.toString();
      } else if (f.isFile()) {
         return "On niisugune fail";
      } else return "Segane lugu" ;
   } // annaVastus lopp

} // DirLoim lopp
 

URL - uniform resource locator

Näide lugemisest (fragment)

      String s = "http://www.cs.ut.ee/";
      URL viide = new URL (s);
      System.out.println ("Protocol = " + viide.getProtocol());
      System.out.println ("Host     = " + viide.getHost());
      System.out.println ("Filename = " + viide.getFile());
      System.out.println ("Port     = " + viide.getPort());
      System.out.println ("Ref      = " + viide.getRef());
      System.out.println ("Sisu:");
      BufferedReader sisend = new BufferedReader
         (new InputStreamReader (viide.openStream()));
      String rida;
      while ((rida = sisend.readLine()) != null)
         System.out.println (rida);
      sisend.close();
 

Pakett javax.net.ssl - Secure Socket Layer

Annab raamid TCP-pistikliidese turvamiseks SSL-protokolli abil.

Jaanus Pöial