November 30, 2022

A Kliens – Szerver szerkezet

A Kliens – Szerver szerkezet

A számítógépek közötti adatcsere leginkább egy beszélgetésre hasonlít. Az
egyik gép kérdez (kliens – a szolgáltatást
igénylő gép), majd a mások gép válaszol (szerver
– szolgáltatási kérést kiszolgáló gép). Mint minden párbeszédnek, ennek
is megvannak a maga szabályai. A Linux az ARPA
modell felépítését használja a kommunikáció lebonyolításához. Az adatok az ARPA
verem szintjein le és fel haladva jutnak az egyik gépből a másikba.

A beszélgetést a kliens gép alkalmazási rétege kezdeményezi. Az adat
először lefelé halad a kliens gép rétegei között, majd a legalsó szinten megtörténik
a fizikai adatátvitel a két gép között. Végül a szerver gépen az adatok a rétegeken
felfelé haladva eljutnak a kiszolgáló alkalmazásig, amelyik a kérdést értelmezve
elkészíti a válasz, majd ugyanazon az útvonalon, amelyen a kérdés érkezett elindítja
visszafelé. A válasz először a szerver gép alkalmazási rétegéből elindul lefelé
a verem rétegein keresztül, majd a legalsó rétegen keresztül megtörténik a fizikai
adatátvitel a kliens gép fel. A kliens gépen, a legalsó rétegen keresztül felfelé
haladva eljut a válasz a kliens gép alkalmazási
réteg
ébe, ahol megkapja a kérdést feltevő alkalmazás.

Az, hogy a kérdés és a válasz milyen típusú és milyen méretű, mindig az alkalmazásoktól
függ. Minden egyes alkalmazás csak a neki megfelelő alkalmazással beszél (pl.
a WWW böngésző
a Web-szerverrel, a levelező kliens viszont a levelező szerverrel).

29. ábra Az adatok útja a kliens-szerver kapcsolatban

A szerverek osztályozása

Több osztályozási kritérium is létezik, ezek közül az egyik a szerver és a
kliens közötti kapcsolat időtartama. Ez alapján két alapvető szerver-típust
külömböztetünk meg:

iteratív szerverek
ebben az esetben a szerver maga kezeli le a kapcsolattartást. ez azt feltételezi,
hogy a kérés időtartalma s a kérés kiszolgálására szolgáló erőforrások megfelelően
kicsik legyenek, mint például az időszolgáltatás esetében.
konkurens szerverek
azok a szolgáltatók, amelyek esetében a kérés számára szükséges erőforrásokat
maga a kérés határozza meg. Ilyen például a nyomtatószerver, a hálózati fájlrendszer,
az adatbázisszerverek. Ezek a szerverek, miután fogadták a kérést, egy újabb
folyamatot indítanak el, amely kiszolgálja a kérést és eközben a szerver maga
újból várakozó állapotba mehet át, az újabb beérkező kérésekre várva.

Egy másik lehetséges kritérium az az információ-mennyiség, amely a szerver
rendelkezésére áll a kliensről. E kritérium alapján is két alapvető típust különböztethetünk
meg:

statefull
ez a szervertípus a kliensekről folyamatosan információkat tárol, tehát tudja,
hogy helyesen működnek-e, vagy nem
stateless
ez a típus nem tárol információkat a klienseiről

Végül a harmadik lehetséges kritérium a hasznélt kapcsolattípus, amely szerint
szintén két fő szerver-csoportot ismerünk:

kapcsolat orientált
ez a szervertípus tipikusan azt feltételezi, hogy a kliens és a szerver fizikailag
egymástól távol vannak és/vagy nagy megbízhatóságra van szükség és emiatt egy
speciális kommunikációs csatornát tartanak fenn.
kapcsolat nélküli
ez tipikusan a helyi hálózatokban működő szerverek esete, ilyen például az
NFS és NIS szolgáltatás

A továbbiakban néhány alapvető szervertípust mutatunk be.

Konkurrens szerverek

Minden, amit itt leírunk pillanatnyilag csak UNIX/LINUX
típusú operációs rendszereken érvényes. Más operációs rendszerek is rendelkeznek
hasonló mechanizmusokkal. Ezek leírását az illető operációs rendszer kézikünyvében
remélhetőleg meg lehet találni.

Amint fentebb már volt szó róla, a konkurrens szerver a kérés beérkezésének
pillanatában a kérés végrehajtását átadja egy külön folyamatnak és a szerver
maga visszakerül a várakozó állapotába, újabb kérésekre várva. Ez tipikus Unix
mechanizmus, az operációs rendszer alapszolgáltatásait használja. ezek közül
a fork() eljárás teszi lehetővé az új folyamatok elindítását. Ennek
a rendszerhívásnak a segítségével egy kérés beérkeztekor a szerver még egy példányban
elindítja magát. Ez a másodpéldány fogja kiszolgálni a beérkezett kérést. A
kérés kiszolgálása után ez a folyamat lezárja saját magát. Ez az alapmechanizmus
teszi lehetővé, hogy mindig legyen egy szerver-folyamat, amely a beérkező kéréseket
figyeli. Természetesen a valós implementációkban a helyzet egy kissé bonyolultabb,
de az alapelv ugyanez.

Statefull szerver

Egy konkrét példája ennek a szervertípusnak az az eset, amikor egy nagyobb
fájlból olvasunk be adatokat. A két alapvető kliens kéréstípus a megnyitás és
az olvasás. A kliens olvasásra való megnyitást kér, válaszul egy leírót kap,
ami a fájl legfontosabb jellemzőit tartalmazza. Majd újabb kéréseket küld a
szervernek. A szerver meg kell jegyezze, hogy ki, milyen jogosultságokkal és
milyen típusú műveletre kért hozzáférést a fájlhoz, és pontosan az állomány
mely részére vonatkozik a kérés, és figyelnie kell, hogy a kliens helyesen működik-e,
ha pedig nem, akkor a kapcsolatot egyoldalűan le kell zárnia.

Stateless szerver

Az előbbi szerver párja az a szolgáltatás, amely csak az adatok megjelenítését
teszi lehetővé, tehát nincs megnyitási kérés, a kliens csak olvasási parancsokat
adhat és neki kell nyilvántartania ka kapcsolat minden fontos adatát.

Melyik a jobb?

A helyes válasz az, hogy attól függ… Például hálózati hiba esetén a stateless
szerver van előnyben, neki nem kell foglalkozzon a felmerülő problémák kezelésével,
ez a kliens dolga. A kliens szempontjából a helyzet pontosan fordított. Más
helyzetek is elképzelhetőek, a gyakorlat viszont azt mutatja, hogy minden konkrét
esetben egyedileg mérlegelni kell, melyik megoldás az előnyösebb. Nincs előre
meghatározott, általánosan alkalmazható recept.

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’]) 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) 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();
    ?>