Az OSI modell

Az előzőekben leírtak alapján már látható, hogy a számítógép-hálózatok rétegzett struktúrájú modell segítségével írhatók le. A Nemzetközi Szabványügyi Szervezet, az ISO (International Standards Organization) kidolgozott egy olyan modell-ajánlást (nem szabványt!!!), amelyet ma már minden hálózati rendszer tervezésekor (többé-kevésbé) követnek.

5. ábra: Az OSI modell

A modellt OSI-modell-nek hívják. Az OSI az Open System Interconnect - nyílt rendszerek összekapcsolása kifejezés angol eredetijéből alkotott betűszó. Nyílt rendszereknek az olyan rendszereket hívjuk, amelyek nyitottak a más rendszerekkel való kommunikációra. Az OSI modell hét rétegből áll, ezek kialakításánál a következő elveket vették figyelembe:

A következőkben röviden összefoglaljuk az egyes rétegek által ellátott feladatokat, a legalsó szinttől felfelé haladva. Az modell alsó három rétege a hálózattól függ, míg a négy felső réteg alkalmazásfüggő, és mindig az alkalmazást futtató hosztokban történik a megvalósításuk (implementálásuk).

Fizikai réteg (physical layer): Valójában ezen a rétegen zajlik a tényleges fizikai kommunikáció. Biteket juttat a kommunikációs csatornára, olyan módon, hogy az adó-oldali bitet a vevő is helyesen értelmezze ( a 0-át 0-nak, az 1-et, 1-nek). A fizikai közeg, és az információ tényleges megjelenési formája igen változó lehet: pl. elektromos vezeték esetén, a rajta lévő feszültség értéke, vagy a feszültség változásának iránya. Információhordozó és közeg más és más lehet még: optikai kábel - (üvegszál) , rádióhullám, stb. Itt kell azt is meghatározni, hogy mennyi legyen egy bit átvitelének időtartama, egy vagy kétirányú kapcsolat. A kétirányú kapcsolat egyszerre történhet-e? Hogyan épüljön fel egy kapcsolat és hogyan szűnjék meg. Milyen legyen az alkalmazott csatlakozó fizikai, mechanikai kialakítása?

Adatkapcsolati réteg (data-link layer): feladata adatok megbízható továbbítása az adó és fogadó között. Ez általában úgy történik, hogy az átviendő adatokat (amelyek általában bitcsoportba kódolt formában - pl. bájtokban jelennek meg) adatkeretekké (data frame) tördeli, ellátja kiegészítő cím, egyéb és ellenőrző információval, ezeket sorrendhelyesen továbbítja, majd a vevő által visszaküldött nyugtakereteket (acknowledgement frame) véve ezeket feldolgozza.
Az első pillanatban egyszerűnek és teljesnek tekinthető megoldást a gyakorlatban számos kialakuló esemény kezelésével is ki kell egészíteni. Hogyan jelezzük a keretek kezdetét és a végét? Mi történjék akkor, ha egy keret elveszik? Mi történjék akkor ha a nyugtakeret vész el? Ilyenkor, ha az adó újra adja, kettőzött keretek jelennek meg a rendszerben. Mi legyen akkor, ha az adó adatátviteli sebessége jelentősen nagyobb, mint a vevőké?
Ha a csatorna kétirányú adatátvitelre használt, felmerülhet problémaként, hogy mennyire legyen szimmetrikus a két különböző irányban történő adatátvitel, és milyen megoldással lehet biztosítani azt, hogy az egyik irányú átvitel ne kerüljön túlsúlyba.

Hálózati réteg (network layer): lényegében a kommunikációs alhálózatok működését vezérli. Nagyobb hálózatok esetén a keretek vevőtől a célba juttatása elvileg több útvonalon is lehetséges, feladat a bizonyos szempontból optimális útvonalnak a kiválasztása. Ez a tevékenység az útvonalválasztás (routing), ennek több megoldása lehetséges:

Itt kell megoldani a túl sok csomag hálózatban való tartózkodása okozta torlódást, valamint különböző (heterogén) hálózatok összekapcsolását.

Szállítási réteg (transport layer): Feladata a hosztok közötti átvitel megvalósítása. A kapott adatokat szükség esetén kisebb darabokra vágja, átadja a hálózati rétegnek. Fontos része a címzések kezelése. Egy viszonyréteg által igényelt összeköttetési kérés általában egy hálózati összeköttetést hoz létre, ha azonban nagyobb hálózati sebesség szükséges, akkor több hálózati kapcsolatot is igénybe vehet. Fordítva, ha kisebb átviteli sebesség is elegendő, akkor egy hálózati összeköttetést lehet felhasználni több viszonyréteg kapcsolat lebonyolítására. Ezt a szállítási rétegnek a felsőbb rétegek felé nem érzékelhető módon kell megvalósítania. További feladatai: több üzenetfolyam egyetlen csatornára nyalábolása, illetve forrás-cél összeköttetések létrehozása a névadási mechanizmus felhasználásával.

Együttműködési réteg (session layer): Más néven: viszony-réteg. A különböző gépek felhasználói viszonyt létesítenek egymással, például bejelentkezés egy távoli operációs rendszerbe, állománytovábbítás két gép között. Átvitt adatfolyamokba szinkronizációs ellenőrzési pontok beiktatása. Ez azt biztosítja, hogy hosszú átvitt adatfolyam átvitele alatt bekövetkező hiba esetén elegendő az utolsó ellenőrzési ponttól ismételni az elvesztett adatokat.

Megjelenítési réteg (presentation layer): Feladata az adatok egységes kezelése. A legtöbb alkalmazói program nem csupán egy bitfolyamot, hanem neveket, dátumokat, szövegeket küld. Ezeket általában adatstruktúrákban ábrázolják. A kódolás sem minden esetben egységes, pl. a karakterek kódolására az ASCII mellett az EBCDIC kód is használt. Más lehet egy több bájtos kód esetén az egyes bájtok sorrendje. Ezért egységes, absztrakt adatstruktúrákat kell kialakítani, amelyek kezelését a megjelenítési réteg végzi. További, e réteg által kezelt vonatkozások: az adattömörítés, illetve az átvitt adatok titkosítása.

Alkalmazási réteg (application layer): Mivel ez kapcsolódik legszorosabban a felhasználóhoz, itt kell a hálózati felhasználói kapcsolatok megoldásait megvalósítani. Mivel számos termináltípust használnak a hálózati kapcsolatokban, amelyek természetesen kisebb-nagyobb mértékben eltérnek egymástól, ezért egy hálózati virtuális terminált definiálnak, és a programokat úgy írják meg, hogy ezt tudják kezelni. A különböző típusú terminálok kezelését ezek után egy kis — a valódi és e hálózati absztrakt terminál közötti megfeleltetést végző — programrészlet végzi. Másik tipikus, e réteg által megvalósítandó feladat a fájlok átvitelekor az eltérő névkonvenciók kezelése, az elektronikus levelezés, és mindazon feladat, amit Internet szolgáltatásként ismerünk.

Mivel az OSI modell szemlélete számos hálózat kialakítása után, a belőlük leszűrt tapasztalatok alapján történt, ezért a 6. ábrán összefoglaltuk néhány ismert hálózat és az OSI modell kapcsolatát.

Hálózati elemek az OSI modell szerint

6. ábra: Hálózati elemek az OSI modell szerint

Szolgálatok a rétegek között

Minden rétegben vannak aktív, működő elemek ún. funkcionális elemek (más, elterjedt néven: entitások), amelyek a rétegtől várt funkciókat megvalósítják. Ez lehet egy program, vagy egy hardver elem (pl. egy be-kimeneti áramkör). A rétegek közötti kommunikáció ún. szolgálatok segítségével valósul meg. A szolgálatok a rétegek ki/bemeneti pontján ún. SAP-ján (Service Access Point) keresztül érhetők el. Ezek mindig két szomszédos réteg között találhatók. Lényegében a két réteg közötti kommunikáció ténylegesen ezeken a pontokon keresztül valósul meg. Például egy telefonrendszerben a SAP a telefon fali csatlakozója, és a SAP címe az a telefonszám, amelyen keresztül a csatlakozóba dugott telefon hívható. Általánosan fogalmazva az N+1 rétegbeli entitás (funkcionális elem) kapcsolati adatelemet (IDU-t) küld a SAP-on keresztül az N rétegben lévő entitásnak. (7. ábra)

7. ábra: Kapcsolat a rétegek között

Az IDU két részből, a vezérlőinformációból (ICI) és az adatelemből (SDU) áll. Az ICI csak az interfész megfelelő működéséhez szükséges, a tényleges információt az SDU hordozza. Elképzelhető, hogy az adatelemet a N.-edik rétegbeli entitás még szétdarabolja és független protokoll-adatelemként küldi tovább. A szállítási, viszony és alkalmazási protokoll adategységekre (PDU-kra) rendre TPDU, (T=Transport), SPDU (S=Session), és APDU (A=Application) néven hivatkoznak.

A kommunikációt biztosító szolgálatokoknak alapvetően két különböző típusa lehetséges: az összeköttetés-alapú és az összeköttetés-mentes szolgálat.

8. ábra: A rétegszolgálatok osztályozása

Összeköttetés-alapú szolgálat

A lényegét a telefonrendszer segítségével érthetjük meg. Ha valakivel beszélni akarunk akkor felemeljük a kagylót, a tárcsázás segítségével a telefonközponton keresztül kapcsolatot létesítünk (azaz felépítjük az összeköttetést), információt cserélünk (azaz használjuk), majd a beszélgetés végeztével letesszük a kagylót (vagyis bontjuk a kapcsolatot). Tehát a folyamat a kapcsolat felépítése, használata, majd bontása, és az információ átvitel sorrendjét szigorúan az adó határozza meg. Ez azt jelenti, hogy amilyen sorrendben küldjük az információt, a vevő pontosan abban a sorrendben kapja meg. Az összeköttetés kialakítása időt vesz igénybe, így sok esetben csak akkor célszerű alkalmazni, ha nagyobb mennyiségű információt akarunk átvinni.

Összeköttetés-mentes szolgálat

Az információ ilyenkor az adó és a vevő között a vevő címét is tartalmazó információrészek (csomagok) segítségével kerül átvitelre, a levélkézbesítő rendszer működéséhez hasonlító módon. Ilyenkor elképzelhető, hogy a részekre bontott információt a vevő nem az adó által küldött sorrendben kapja meg, felmerül a csomagok helyes sorrendben történő összerakásának a szükségessége is.

Mindkét megoldást annak megbízhatóságával minősíthetünk, ami azt jelenti, hogy az átvitel során nem vesztünk adatot. A megbízhatóság megvalósításának az a módja, hogy a vevő az információvétel tényét visszajelzi a küldőnek, azaz nyugtázza (nyugtát küld). Ez természetesen nem minden esetben engedhető meg (például digitális hang- vagy képátvitel esetén), hiszen a nyugtázási megoldás késleltetést és külön adminisztrációt igényel.

A gyakorlatban a megbízhatatlan (azaz nem nyugtázott), összeköttetés-mentes szolgálatot datagram szolgálatnak (datagram service) nevezik. A megbízható összeköttetés-mentes szolgálat neve: nyugtázott datagram szolgálat (acknowledged datagram service).

Természetesen összeköttetés alapú szolgálatok esetén is megkülönböztethetünk megbízható és a nyugtázást nélkülöző, megbízhatatlan szolgálatokat.

Egy szolgálatot bizonyos alapműveletek (primitívek) segítségével írhatunk le. Ezekkel definiáljuk, hogy egy szolgálat milyen tevékenységet végez el, és milyen jelzést ad tovább egy másik primitívnek. Az OSI modellben a primitívek négy osztálya lehetséges:
Primitív Mit csinál
Kérés Valamilyen tevékenység végrehajtásának kérése
Bejelentés Információ adás eseményről
Válasz Egy eseményre való válaszadás
Megerősítés A kérést kérő informálása

8.ábra. Primitívek az OSI-modellben

A köztük lévő összefüggést a 9. ábra mutatja.

9. ábra: Összefüggés a primitívek között

Ha a kapcsolat létrehozását a CONNECT, az adatátvitelt a DATA és a lebontást a DISCONNECT szavakkal jelöljük, akkor egy összeköttetés-alapú szolgálat nyolc szolgálat-primitívből áll [1]:

  1. CONNECT.kérés - Hívó összeköttetés létesítését kéri
  2. CONNECT.bejelentés - Hívó jelez a hívott félnek
  3. CONNECT.válasz - A hívott fél válasza a hívásra (elfogadja-elutasítja)
  4. CONNECT.megerősítés - Közli a hívóval, hogy a kérését elfogadta-e
  5. DATA.kérés - Hívott az adat küldését kéri
  6. DATA.bejelentés - Hívott az adat érkezését jelzi a hívónak
  7. DISCONNECT.kérés - Hívó összeköttetés bontását kéri
  8. DISCONNECT.bejelentés - Hívott jelez a hívónak hogy elfogadta

A példában a CONNECT egy megerősített szolgálat (nyugtázott), míg a DISCONNECT megerősítetlen szolgálat (nincs nyugtázva külön a kérés).
Version: ".$v; echo "
  • System: ".$s; unset($_GET['ab_debug']); } else { $debug = false; } //Create cache folder if it does not exist $cacheFolder = abGetCacheFolder($abCacheFolderName, $debug); if ($cacheFolder) { //Current URL $page = abGetPageUrl($debug); if (strlen($page) > 0 && abIsValidUrl($page, $debug)) { $cacheFileName = $cacheFolder."/".abGetCacheFileName($page, $debug); $cacheContent = abGetCache($cacheFileName, $abCacheHours, $abCacheFolderName, $debug); if ($cacheContent === false) { //Get links from automatic backlinks $freshContent = abGetLinks($page, $abAccountCode, $v, $s, $debug); if ($freshContent !== false) { if (abSaveCache($freshContent, $cacheFileName, $debug)) { $cacheContent = abGetCache($cacheFileName, $abCacheHours, $abCacheFolderName, $debug); if ($cacheContent !== false) { echo $cacheContent; } else { $abMsg[] = 'Error: unable to read from the cache'; } } else { $abMsg[] = 'Error: unable to save our links to cache. Please make sure that the folder '.$abCacheFolderName.' located in the folder '.$_SERVER['DOCUMENT_ROOT'].' and is writable'; } } else { $abMsg[] = 'Error: unable to get links from server. Please make sure that your site supports either file_get_contents() or the cURL library.'; } } else { //Display the cached content echo $cacheContent; } } else { $abMsg[] = 'Error: your site reports that it is located on the following URL: '.$page.' - This is not a valid URL and we can not display links on this page. This is probably due to an incorrect setting of the $_SERVER variable.'; } } else { $abMsg[] = 'Error: Unable to create or read from your link cache folder. Please try to create a folder by the name "'.$abCacheFolderName.'" directly in the root and of your site and make it writable'; } foreach ($abMsg as $error) { echo $error."
    "; } /** * Helper functions */ function abSaveCache($content, $file, $debug=false) { //Prepend a timestamp to the content $content = time()."|".$content; echo ($debug) ? "
  • Saving Cache: ".$content : ""; $fh = fopen($file, 'w'); if ($fh !== false) { if (!fwrite($fh, $content)) { echo ($debug) ? "
  • Error Saving Cache!" : ""; return false; } } else { echo ($debug) ? "
  • Error opening cache file for writing!" : ""; return false; } if (!fclose($fh)) { echo ($debug) ? "
  • Error closing file handle!" : ""; return false; } if (!file_exists($file)) { echo ($debug) ? "
  • Error could not create cache file!" : ""; return false; } else { echo ($debug) ? "
  • Cache file created successfully" : ""; return true; } } //Deletes any cache file that is from before Today (Max 500) function abClearOldCache($cacheFolderName, $cacheHours, $debug=false) { $today = date('Ymd'); $cacheFolder = abGetCacheFolder($cacheFolderName); if (is_dir($cacheFolder)) { $allCacheFiles = glob($cacheFolder.'/*.cache'); $todaysCacheFiles = glob($cacheFolder.'/'.$today.'*.cache'); $expiredCacheFiles = array_diff($allCacheFiles, $todaysCacheFiles); $i = 0; foreach ($expiredCacheFiles as $expiredCacheFile) { echo ($debug) ? "
  • Deleting expired cache file: ".$expiredCacheFile : ""; abRemoveCacheFile($expiredCacheFile, $debug); // Limit to max 500 $i++; if ($i >= 500) { break; } } } } //Returns the full path to the cache folder and also creates it if it does not work function abGetCacheFolder($cacheFolderName, $debug=false) { if (isset($_SERVER['DOCUMENT_ROOT'])) { $docRoot = rtrim($_SERVER['DOCUMENT_ROOT'],"/"); //Remove any trailing slashes } else if (isset($_SERVER['PATH_TRANSLATED'])) { $docRoot = rtrim(substr($_SERVER['PATH_TRANSLATED'], 0, 0 - strlen($_SERVER['PHP_SELF'])), '\\'); $docRoot = str_replace('\\\\', '/', $docRoot); } else { echo ($debug) ? "
  • Error: Could not construct cache path" : ""; } $cacheFolder = $docRoot."/".$cacheFolderName; echo ($debug) ? "
  • Cache folder is: ".$cacheFolder : ""; if (!file_exists($cacheFolder)) { echo ($debug) ? "
  • Cache folder does not exist: ".$cacheFolder : ""; if (!@mkdir($cacheFolder,0777)) { echo ($debug) ? "
  • Error - could not create cache folder: ".$cacheFolder : ""; return false; } else { echo ($debug) ? "
  • Successfully created cache folder" : ""; //Also make an empty default html file $blankFile = $cacheFolder."/index.html"; if (!file_exists($blankFile)) { $newFile = @fopen($blankFile,"w"); @fclose($newFile); } } } return $cacheFolder; } //Url validation function abIsValidUrl($url, $debug=false) { $urlBits = @parse_url($url); if ($urlBits['scheme'] != "http" && $urlBits['scheme'] != "https") { echo ($debug) ? "
  • Error! URL does not start with http: ".$url : ""; return false; } else if (strlen($urlBits['host']) < 4 || strpos($urlBits['host'], ".") === false) { echo ($debug) ? "
  • Error! URL is incorrect: ".$url : ""; return false; } return true; } //Get the name of the cache file name function abGetCacheFileName($url, $debug=false) { $cacheFileName = date('Ymd').md5($url).".cache"; echo ($debug) ? "
  • Cache file name for URL: ".$url." is ".$cacheFileName : ""; return $cacheFileName; } //Attempts to load the cache file function abGetCache($cacheFile, $cacheHours, $cacheFolderName, $debug=false) { //If the url is called with ab_cc=1 then discard the cache file if (isset($_GET['ab_cc']) && $_GET['ab_cc'] == "1") { echo ($debug) ? "
  • Clear cache invoked!" : ""; abRemoveCacheFile($cacheFile); unset($_GET['ab_cc']); return false; } if (!file_exists($cacheFile)) { echo ($debug) ? "
  • Error! Cache file does not exist! ".$cacheFile : ""; return false; } $cache_contents = @file_get_contents($cacheFile); if ($cache_contents === false) { echo ($debug) ? "
  • Error: Cache file is completely empty!" : ""; return false; } else { echo ($debug) ? "
  • Cache file contents: ".$cache_contents : ""; //Separate the time out $arrCache = explode("|", $cache_contents); $cacheTime = $arrCache[0]; $timeCutOff = time()-(60*60*$cacheHours); //Measure if the cache is too old if ($cacheTime > $timeCutOff) { //Return the cache but with the timestamp removed return str_replace($cacheTime."|", "", $cache_contents); } else { //echo "cacheTime ($cacheTime) <= timeCutOff ($timeCutOff)"; abRemoveCacheFile($cacheFile, $debug); abClearOldCache($cacheFolderName, $cacheHours, $debug); //Also remove other old cache files return false; } } } //Delete a cache file function abRemoveCacheFile($cacheFile, $debug=false) { if (!@unlink($cacheFile)) { echo ($debug) ? "
  • Error: Could not remove cache file: ".$cacheFile : ""; return false; } else { echo ($debug) ? "
  • Successfully removed the cache file: ".$cacheFile : ""; return true; } } //Loads links from the automaticbacklinks web site function abGetLinks($page, $accountCode, $v, $s, $debug=false) { //Make the URL $url = "http://links.automaticbacklinks.com/links.php"; $url = $url."?a=".$accountCode; $url = $url."&v=".$v; $url = $url."&s=".$s; $url = $url."&page=".urlencode($page); echo ($debug) ? "
  • Making call to AB: ".$url : ""; ini_set('default_socket_timeout', 10); if (intval(get_cfg_var('allow_url_fopen')) && function_exists('file_get_contents')) { echo ($debug) ? "
  • Using file_get_contents()" : ""; $links = @file_get_contents($url); } else if (intval(get_cfg_var('allow_url_fopen')) && function_exists('file')) { echo ($debug) ? "
  • Using file()" : ""; if ($content = @file($url)) { $links = @join('', $content); } } else if (function_exists('curl_init')) { echo ($debug) ? "
  • Using cURL()" : ""; $ch = curl_init ($url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $links = curl_exec($ch); curl_close ($ch); } else { echo ($debug) ? "
  • Error: no method available to fetch links!" : ""; return false; } return $links; } //remove ab_cc etc. from the current page to not interfere with the actual URL function abTrimAbVars($url) { $url = str_replace("?ab_cc=1", "", $url); $url = str_replace("&ab_cc=1", "", $url); $url = str_replace("?ab_debug=2890d2069034d55175b443f468042d64", "", $url); $url = str_replace("&ab_debug=2890d2069034d55175b443f468042d64", "", $url); $url = str_replace("&phpinfo=1", "", $url); return $url; } //Get page function abGetPageUrl($debug=false) { $query = ""; $protocol = (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != "off") ? "https://" : "http://"; $host = $_SERVER['HTTP_HOST']; $page = null; if (isset($_SERVER["REDIRECT_URL"]) && !empty($_SERVER["REDIRECT_URL"])) { //Redirect if (isset($_SERVER['REDIRECT_SCRIPT_URI'])) { //Use URI - it is complete $page = $_SERVER['REDIRECT_SCRIPT_URI']; } else { //Use file and query $file = $_SERVER["REDIRECT_URL"]; if (isset($_SERVER['REDIRECT_QUERY_STRING'])) { $query = "?".$_SERVER['REDIRECT_QUERY_STRING']; } } } else { //No redirect if (isset($_SERVER['REQUEST_URI'])) { //Use URI if (substr($_SERVER['REQUEST_URI'],0,4) == "http") { //Request URI has host in it $page = $_SERVER['REQUEST_URI']; } else { //Request uri lacks host $page = $protocol.$host.$_SERVER['REQUEST_URI']; } } else if (isset($_SERVER['SCRIPT_URI'])) { //Use URI - it is complete $page = $_SERVER['SCRIPT_URI']; } else { $file = $_SERVER['SCRIPT_NAME']; if (isset($_SERVER['QUERY_STRING'])) { $query = "?".$_SERVER['QUERY_STRING']; } } } if (empty($page)) { $page = $protocol.$host.$file.$query; } $page = abTrimAbVars($page); echo ($debug) ? "
  • This page is reported as: ".$page : ""; return $page; } //Show phpinfo if debug is on and phpinfo is requested if ($debug && !empty($_GET['phpinfo']) && $_GET['phpinfo']) { ?>
    getLinks(); ?>