6.
A szállítási réteg
A szállítási réteg nem csak a hétrétegű architektúra egy újabb rétege, hanem az egész protokollhierarchia legfontosabb rétege. Feladata az, hogy megbízható, gazdaságos adatszállítást biztosítson a forráshoszttól a célhosztig, függetlenül magától a fizikai hálózattól vagy az aktuálisan használt kommunikációs alhálózatoktól. A szállítási ré teg nélkül a rétegezett protokollkoncepciónak nem sok értelme lenne. Ebben a fejezet ben a szállítási réteget fogjuk részletesen tanulmányozni, beleértve annak szolgálatait, tervezését, protokolljait és teljesítőképességét.
6.1.
A szállítási szolgálat
A következő néhány alfejezet a szállítási szolgálat alapjait mutatja be. Áttekintjük, hogy milyen szolgálatokat kínál az alkalmazási réteg (vagy ha egyáltalán van, a vi szony réteg) felé, különös tekintettel a szolgáltatás minőségének jellemzésére. Végül megvizsgáljuk, hogy az alkalmazások hogyan érik el a szállítási szolgálatokat, azaz milyen interfész áll rendelkezésükre.
6.1.1.
A felső rétegeknek nyújtott szolgálatok
A szállítási réteg legfőbb célja az, hogy hatékony, megbízható és gazdaságos szolgála tot nyújtson felhasználóinak, általában az alkalmazási rétegben futó folyamatoknak. E cél érdekében a szállítási réteg felhasználja a hálózati réteg által nyújtott szolgálato kat. A szállítási rétegen belül azt a hardver és/vagy szoftver elemet, amely a munkát végzi, szállítási funkcionális elemnek vagy szállítási entitásnak (transport entity) nevezzük. Ez lehet az operációs rendszer magjának (kernelének) része, önálló felhasz nálói folyamat, egy hálózati alkalmazáshoz tartozó könyvtár vagy a hálózati illesztő kártya. A hálózati, szállítási és alkalmazási réteg kapcsolatát a 6.1. ábra szemlélteti. Ahogy a hálózati szolgálatoknak két típusa van, összeköttetés alapú és összeköttetés nélküli, ugyanígy kétféle szállítási szolgálat létezik. Az összeköttetés alapú szállítási szolgálat sok tekintetben hasonló az összeköttetés alapú hálózati szolgálathoz. Mind-
527
A SZÁLLÍTÁSI RÉTEG
2. hoszt
1. hoszt Alkalmazási (vagy viszony) réteg Szállítási cím
Alkalmazási szállítási interfész
Alkalmazási (vagy viszony) réteg
/ TPDU
Szállítási entitás
D-
Szállítási entitás
Szállítási protokoll Hálózati cím Hálózati réteg
V Szállítási hálózati interfész
Hálózati réteg
6.1. ábra. A hálózati, szállítási és alkalmazási réteg
két esetben az összeköttetésnek három fázisa van: létesítés, adatátvitel és lebontás. A címzés és forgalomszabályozás szintén hasonló a két rétegben. Az összeköttetés nélküli szállítási szolgálat is nagyon hasonló az összeköttetés nélküli hálózati szolgálathoz. A nyilvánvaló kérdés ezután az, hogy ha a szállítási réteg szolgálata ennyire ha sonló a hálózati réteg szolgálatához, akkor miért van mégis két külön réteg. Miért nem elegendő egy? A válasz egy apró, de lényeges különbségben rejlik, amelyhez az 1.9. ábrára kell visszautalnunk. A szállítási réteg kódja teljes egészében a felhasználók gé pein fut, szemben a hálózati réteggel, ami nagyrészt a routereken, a routereket pedig a szolgáltató üzemelteti (legalábbis a nagy kiterjedésű hálózatokban). Mi történik, ha a hálózati réteg nem nyújt megfelelő szolgáltatásminőséget? Esetleg gyakran veszti el a csomagokat? Mi történik, ha a routerek időről időre lefagynak? Ezekben az esetekben bizony bajok történnek. A felhasználóknak nincs igazi bele szólása a hálózati réteg működésébe, ezért nem tudják azzal megoldani a gyenge mi nőségű szolgálat problémáját, hogy több routert vagy jobb hibakezelést építenek be a hálózati rétegbe. Az egyetlen lehetőség az, hogy egy olyan másik réteget építsünk rá a hálózati rétegre, amely javítja a szolgáltatásminőséget. Ha egy összeköttetés alapú alhá lózatban a szállítási entitás egy hosszú átvitel közepén arról értesül, hogy hálózati öszszeköttetése hirtelen megszakadt, akkor sehogyan sem tudja meghatározni, hogy mi tör tént az éppen úton levő csomagokkal. Ezért új hálózati összeköttetést kell kiépítenie, amelyen azután egy lekérdezéssel megkérdezheti a társentitástól, hogy mely adatok ér keztek meg és melyek nem. Ezután az átvitelt ott folytathatják, ahol az félbemaradt. A szállítási réteg létezése lényegében azt teszi lehetővé, hogy a szállítási szolgálat megbízhatóbb lehessen annál a hálózati szolgálatnál, amelyre ráépül. A szállítási réteg képes felfedezni és kiegyenlíteni az elveszett csomagok és a csonkolt adatok okozta hibákat. Mindezen felül a szállítási szolgálat primitívjei könyvtári függvényhívások ként is megvalósíthatók, és ezzel függetleníthetó'k a hálózati szolgálat primitívjeitől. A hálózati szolgálat hívásai az egyes hálózatokban jelentősen eltérhetnek (egy össze-
528
SZÁMÍTÓGÉP-HÁLÓZATOK
köttetés nélküli LAN-szolgálat például jelentősen különbözhet egy összeköttetés alapú WAN-szolgálattól). A hálózati szolgálat részletei rejtve maradnak a szállítási szolgálat primitívjei mögött. A hálózati szolgálat lecserélésekor így mindössze arra van szük ség, hogy a könyvtári függvények egyik készletét lecseréljük egy másikra, amely egy másik szolgálatra ráépülve látja el ugyanazt a feladatot. A szállítási rétegnek köszönhetően az alkalmazások programozói egy szabványos primitívkészletre írhatják a kódot, és az így megírt programok a hálózatok széles ská láján működnek. Mindezt anélkül, hogy a programozóknak a különféle alhálózati in terfészekkel és a megbízhatatlan átvitellel törődniük kellene. Ha minden létező háló zat tökéletes lenne, és mindegyik egy olyan közös szolgálatprimitív-készletet használ na, amely garantáltan soha de soha nem változik, akkor lehet, hogy nem lenne szükség a szállítási rétegre. A gyakorlati életben azonban azt a kulcsfontosságú feladatot telje síti, hogy elszigeteli a magasabb rétegeket a műszaki megoldásoktól és az alhálózat tökéletlenségeitől. A fenti ok miatt sokan megkülönböztetik az 1-4. rétegeket a 4. feletti réteg(ek)től. Az alsó négy réteget tekinthetjük a szállítási szolgáltatónak (transport service provider), míg a magasabb réteg(ek)et tekinthetjük a szállítási szolgálat felhasználó jának (transport service user). A szolgáltató és a felhasználó ezen elkülönítése je lentős hatással van a rétegek kialakítására, és kulcsfontosságú helyzetbe hozza a szál lítási réteget, mivel ez alkotja a fő határvonalat a szolgáltató és a megbízható adatát viteli szolgálat felhasználója között.
6.1.2.
Szállítási szolgálati primitívek
A szállítási rétegnek néhány műveletet, vagyis egy szállítási szolgálati interfészt kell biztosítania az alkalmazási programok számára annak érdekében, hogy a felhasználók hozzáférhessenek a szolgálataihoz. Minden szállítási szolgálat egyedi interfésszel ren delkezik. Ebben a szakaszban először egy egyszerű (hipotetikus) szállítási szolgálatot és annak interfészét fogjuk megvizsgálni, hogy bemutassuk a legalapvetőbb jellegzetes ségeket. A következő szakaszban pedig egy gyakorlati példával ismerkedünk majd meg. A szállítási szolgálat hasonlít a hálózati szolgálathoz, azonban van néhány fontos eltérés. A fő különbség köztük az, hogy a hálózati szolgálat a valódi hálózatok által nyújtott szolgáltatásokat igyekszik modellezni azok gyengéivel együtt. Az igazi háló zatok csomagokat veszíthetnek, tehát a hálózati szolgálat általában nem megbízható. Ezzel szemben az (összeköttetés alapú) szállítási szolgálat megbízható. Természe tesen a valódi hálózatok nem hibamentesek, de pontosan a szállítási réteg feladata az, hogy egy nem megbízható hálózatra épülve megbízható szolgálatot nyújtson. Vegyünk például két olyan folyamatot, amelyek a UNIX-ban csövekkel vannak összekötve. Ezek azt feltételezik, hogy a köztük levő összeköttetés tökéletes. Hallani sem akarnak nyugtázásokról, elvesztett csomagokról, torlódásról vagy más, ehhez ha sonló problémáról. Egy 100 százalékosan megbízható összeköttetést akarnak használni. Az A folyamat beteszi az adatokat a cső egyik végén, a B folyamat kiveszi a másik vé gén. Ez a lényege a szállítási szolgálatnak: elrejteni a hálózati szolgálat hiányosságait, hogy az alkalmazási folyamatok hibamentes bitfolyamot feltételezhessenek.
529
A SZÁLLÍTÁSI RÉTEG
Primitív
Elküldött TPDU
Jelentés
LISTEN
(nincs)
Vár, amíg egy folyamat kapcsolódni nem próbál
CONNECT
CONNECTION REQ.
Összeköttetést próbál létrehozni
SEND
DATA
Adatot küld
RECEIVE
(nincs)
Vár, amíg adat (DATA TPDU) nem érkezik
DISCONNECT
DISCONNECTION REQ.
Ez az oldal bontani kívánja az összeköttetést
6.2. ábra. Egy egyszerű szállítási szolgálat primitívjei
A szállítási réteg járulékos tulajdonsága, hogy megbízhatatlan (datagram) szolgá latot is tud nyújtani. Erről azonban viszonylag keveset lehet mondani, így a figyel münket ebben a fejezetben főként az összeköttetés alapú szállítási szolgálatokra fog juk irányítani. Ennek ellenére van néhány olyan alkalmazás (mint például a kliens-szer ver-alkalmazások és a közvetítéses multimédia-szolgáltatás) amelyekhez előnyös az öszszeköttetés nélküli szállítás, ezért egy keveset még fogunk beszélni erről a későbbiekben. Egy másik különbség a hálózati és a szállítási szolgálat között a szolgálat fel használóinak köre. A hálózati szolgálatot csak a szállítási entitások használják. Keve sen írják meg a saját szállítási entitásaikat, ezért kevés program látja a csupasz hálóza ti szolgálatot. Ezzel ellentétben viszont sok alkalmazás (ezzel együtt programozó) használja a szállítási primitíveket, ezért a szállítási szolgálatnak kényelmesnek és könnyen használhatónak kell lennie. A 6.2. ábrán bemutatunk öt lehetséges szállítási primitívet. Ez a szállítási szolgálat csak egy puszta váz, de ízelítőt ad egy összeköttetés alapú szállítási interfész lényeges feladataiból. Lehetővé teszi a felhasználói programoknak összeköttetések létesítését, használatát és lebontását, ami a legtöbb alkalmazásnak elegendő is. Hogy a primitívek működésére is lássunk példát, vegyünk egy alkalmazást egy szerverrel és több távoli klienssel. Először a szerver egy LISTEN (FIGYELÉS) primitívet hajt végre, tipikusan egy könyvtári függvényhívással, ami rendszerhívást eredményez, hogy a szerver egy kliens jelentkezéséig blokkolódjon. Amikor egy kliens beszélni akar a szerverrel, egy CONNECT (KAPCSOLÁS) primitívet hajt végre. A szállítási entitás ezt úgy valósítja meg, hogy a hívót blokkolja, és egy csomag adatmezejébe ágyazott szállítási üzenetet küld a szerver szállítási entitása részére. Itt rövid terminológiai kitérőt kell tennünk. Jobb híján az esetlen TPDU (Transport Protocol Data Unit - szállítási protokoll adategység) elnevezést kényszerü lünk használni szállítási entitások közötti üzenetekre. Ezek a TPDU-k (melyeket a szállítási réteg küld és fogad) csomagokba (amiket a hálózati réteg használ) vannak beágyazva. A csomagok viszont (adatkapcsolati réteg által kezelt) keretekben (frame) foglalnak helyet. Amikor egy keret megérkezik, az adatkapcsolati réteg földolgozza a keret fejrészét, és a keret adatmezejének tartalmát továbbadja a hálózati entitásnak. A hálózati entitás földolgozza a csomag fejrészét, és az adatmező tartalmát átadja a szál lítási entitásnak. Ezt a beágyazott struktúrát szemlélteti a 6.3. ábra.
530
SZÁMÍTÓGÉP-HÁLÓZATOK
Keretfejrész
4-
Csomagfejrész
TPDU fejrész
~zi TPDU adatmező
Csomag adatmező Keret adatmező 6.3. ábra. A TPDU-k, csomagok és keretek beágyazása
Visszatérve a kliens-szerver példához, a kliens CONNECT hívása hatására a szállítá si entitás CONNECTION REQUEST (ÖSSZEKÖTTETÉS-KÉRÉS) TPDU-t küld a szerver felé. Amikor az megérkezik, a szállítási entitás meggyőződik arról, hogy a szerver LISTEN hívásban várakozik (azaz kész kéréseket kiszolgálni). Ekkor megszünteti a szerver blokkolását, és CONNECTION ACCEPTED (összekötetés kérés elfogadva) TPDU-t küld vissza a kliensnek. Amint a TPDU megérkezik, a kliens blokkolása is feloldódik, így az összeköttetés létrejön. Ekkor megkezdődhet az adatátvitel a SEND és RECEIVE (ADÁS és VÉTEL) primitívek segítségével. A legegyszerűbb esetben bármelyik fél végrehajthat egy (blokkoló) RECEIVE hívást, hogy várakozzon a partner által (SEND primitívvel) küldött adatra. Amikor a TPDU megérkezik, a fogadó blokkolása megszűnik, földolgozza a kapott adatot, és választ küld. Amíg mindkét fél nyomon tudja követni, hogy ki mikor követ kezik, ez a rendszer jól működik. Megjegyezzük, hogy a hálózati rétegben még egy egyszerű egyirányú adatforga lom is jóval bonyolultabb, mint a szállítási rétegben. Minden adatcsomagot nyugtáz nak, sőt a vezérlő TPDU-kat hordozó csomagokra is érkezik közvetett vagy közvetlen nyugtázás. Ezeket a nyugtázásokat a szállítási entitások kezelik a hálózati rétegbeli protokollok segítségével, és a szállítási felhasználók számára ezek nem láthatók. Ha sonlóan, a szállítási entitásoknak kell foglalkozniuk az időzítésekkel és az ismétlések kel. Ezen mechanizmusokból szintén semmit sem látnak a szállítási felhasználók. Szá mukra az összeköttetés egy megbízható csővezeték, azaz: amit egy felhasználó a cső egyik végén betölt, az a másik végén változatlanul megjelenik. A bonyolultság elrejtésé nek képessége miatt a rétegezett protokollok nagyon hatékony eszköznek bizonyulnak. Amikor egy összeköttetésre többé nincs szükség, azt le kell bontani, hogy ne fog laljon fölöslegesen táblahelyet a két szállítási entitáson belül. A bontásnak két változa ta van: aszimmetrikus és szimmetrikus. Az aszimmetrikus esetben valamelyik szállítási felhasználó kiad egy DISCONNECT primitívet, aminek hatására a szállítási entitás egy DISCONNECT (ÖSSZEKÖTTETÉS-BONTÁS) TPDU-t küld a távoli szállítási entitásnak. A TPDU megérkezésekor az összeköttetés lebomlik. A szimmetrikus esetben mindkét irányt külön, a másiktól függetlenül zárják le. Amikor az egyik fél DISCONNECT hívást kezdeményez, az azt jelenti, hogy nincs több elküldenivaló adata, de továbbra is hajlandó partnere adatait fogadni. Ebben a modell ben az összekötetés akkor ér véget, amikor mindkét fél végrehajtotta a DISCONNECT primitívet.
531
A SZÁLLÍTÁSI RÉTEG
Az összeköttetés létesítés primitív végrehajtva
Összeköttetés kérés TPDU érkezett TÉTLEN
AKTÍV LÉTESÍTÉS FOLYAMATBAN
PASSZÍV LÉTESÍTÉS FOLYAMATBAN
_ ÖSSZEKÖTTETÉS LÉTREJÖTT Az összeköttetés-létesítés Összeköttetés elfogadva primitív végrehajtva TPDU érkezett Összeköttetés-bontás Az összeköttetés bontása kérés TPDU érkezett primitív végrehajtva AKTÍV BONTÁS FOLYAMATBAN
PASSZÍV BONTÁS h-FOLYAMATBAN
TÉTLEN Az összeköttetés-bontás primitív végrehajtva
Összeköttetés-bontás kérése TPDU érkezett
6.4. ábra. Egyszerű összeköttetés-kezelés állapotdiagramja. A dőlt betűvel szedett állapotátmeneteket beérkező csomagok váltják ki. A folytonos nyilak a kliens, a szaggatott nyilak a szerver állapotátmeneteit mutatják
Ezen egyszerű primitíveken alapuló összeköttetés-létesítés és -bontás állapotdia gramja látható a 6.4. ábrán. Minden állapotátmenetet valamilyen esemény vált ki: vagy egy, a helyi felhasználó által végrehajtott primitív, vagy egy beérkező csomag. Az egyszerűség kedvéért föltételezzük, hogy minden TPDU nyugtázása külön törté nik. Feltesszük továbbá, hogy szimmetrikus összeköttetés-bontást modellezünk úgy, hogy a kliens kezdeményez. Megjegyzendő, hogy az ábra meglehetősen elnagyolt. Később megvizsgálunk egy ennél valósághűbb modellt is.
6.1.3.
Berkeley TCP-primitívek
Vizsgáljunk meg röviden egy másik szállítási primitívkészletet, a Berkeley UNIX-ban használt TCP-socket primitíveket. Ezeket a 6.5. ábrán is felsorolt primitíveket széles körűen alkalmazzák az Internet programozásában. Nagyvonalakban követik az első példában bemutatott modellt, de több lehetőséget és rugalmasságot nyújtanak. Itt nem térünk ki a megfelelő TPDU-kra, annak tárgyalására a TCP tanulmányozása után ke rül sor e fejezet későbbi részében. Az első négy primitívet az ábrán látható sorrendben hajtja végre a szerver. A
532
SZÁMÍTÓGÉP-HÁLÓZATOK
Primitív
Jelentés
SOCKET
Uj kommunikációs végpont (csatlakozó) létrehozása
BIND
Helyi cím hozzárendelése a csatlakozóhoz
LISTEN
Összeköttetés-elfogadási szándék bejelentése, várakozási sor hosszának megadása
ACCEPT
Hívó blokkolása összeköttetés-létesítési kísérletig
CONNECT
Próbálkozás összeköttetés-létesítésre
SEND
Adatküldés az összeköttetésen keresztül
RECEIVE
Adatfogadás az összeköttetésről
CLOSE
Összeköttetés bontása
6.5. ábra. TCP-socket primitívek SOCKET primitív új végpontot (csatlakozót) hoz létre, és táblahelyet foglal le a szállítá si entitásban. A hívás paraméterei rögzítik a használni kívánt címzési formát, a szol gálat típusát (pl. megbízható bitfolyam) és a protokollt. A sikeres SOCKET hívás kö zönséges állományleíróval tér vissza, amit a további hívások használnak éppúgy, mint az OPEN rendszerhívásnál. Az újonnan létrehozott csatlakozóknak (socket) nincs címük, a hozzárendelést a BIND primitív végzi. Amint a szerver címet rendelt a végponthoz, távoli kliensek csat lakozhatnak hozzá. Annak oka, hogy a cím megadása nem a SOCKET hívással történik az, hogy vannak olyan folyamatok, amelyek számára fontosak a címek (pl. évek óta ugyanazt a címet használják, és ez a cím mindenki által ismert), míg mások számára a cím megválasztása közömbös. Ezt követi a LISTEN hívás, amely a beérkező hívások várakozási sorának foglal he lyet arra az esetre, amikor a szerverhez egy időben több kliens is kapcsolódni kíván. Ellentétben az első példában használt LlSTEN-nel, a TCP-modellben a LISTEN nem blokkoló hívás. A szerver ACCEPT primitívet hajt végre ahhoz, hogy blokkolja magát egy bejövő összeköttetés-kérésig. Amikor egy összeköttetést kérő TPDU érkezik, a transzporten titás az eredetivel azonos tulajdonságokkal rendelkező új végpontot hoz létre, és hoz zárendel egy állományleírót. A szerver ekkor új folyamatot vagy szálat indít az új vég ponton létrejövő kapcsolat kezelésére, és tovább várja a következő kérést az eredeti végponton. Az ACCEPT egy szokványos állományleírót ad vissza, amelyet ezután a megszokott módon lehet írásra és olvasásra használni ugyanúgy, mint a tényleges ál lományok leíróit. Most vegyük szemügyre a kliensoldalt. Itt ugyancsak egy végpontot kell először létrehozni a SOCKET primitív segítségével, de BIND hívás nem szükséges, mivel a hasz nált cím nem érdekli a szervert. A CONNECT primitív blokkolja a hívót, és belekezd az összeköttetés-létesítési folyamatba. Amikor ezt befejezi (azaz a megfelelő TPDU megérkezett a szervertől), a kliensfolyamat blokkolása megszűnik, és az összeköttetés létrejön. Ekkor mindkét fél a SEND és RECEIVE primitív segítségével küldhet és fogad-
A SZÁLLÍTÁSI RÉTEG
533
hat adatokat a duplex összeköttetésen keresztül. Amennyiben a SEND és a RÉCÉIVÉ különleges lehetőségeire nincsen szükség, a UNIX szabványos REÁD és WRITE rend szerhívásait is lehet használni. Az összeköttetés bontása szimmetrikus. Amikor mindkét fél végrehajtotta a CLOSE primitívet, az összeköttetés megszűnik.
6.1.4.
Csatlakozó-programozási példa: egy internetes állományszerver
A csatlakozók (socket) hívásainak használatát a 6.6. ábrán megadott kliens és szerver kódján keresztül mutatjuk be. Az ábrán egy nagyon egyszerű internetes állományszerver látható, valamint egy példa-kliens, amely ezt a szervert használja. A kódnak számos hiá nyossága van (ezeket meg fogjuk tárgyalni), de elméletileg a szerver kódját bármely inter netre kötött UNIX rendszeren le lehet fordítani és le is lehet futtatni. Ezután a kliens kódja is lefordítható és futtatható a világ bármely másik UNIX-os gépén. A kliens kódja a meg felelő' paraméterekkel indítva bármely olyan állományt le tud tölteni a szerverről, amely hez annak a saját gépén hozzáférése van. Az állományt a kliens a standard outputra teszi, amelyet természetesen tovább irányíthatunk egy állományba vagy egy csővezetékbe. Elsőként vizsgáljuk meg a szerver kódját! Az eleje néhány szabványos könyvtár illesztést tartalmaz, amelyek közül az utolsó három tartalmazza a legfőbb Internettel kapcsolatos definíciókat és adatszerkezeteket. Ezután a SERVER_PORT definíciója következik. Az 12345-ös portot jelöltük ki erre a célra, de ez a szám tetszőleges. Bár mely olyan 1024 és 65535 közötti portszám ugyanilyen alkalmas erre a célra, amelyet más folyamat nem használ. Természetesen a kliensnek és a szervernek azonos portot kell használnia. Amennyiben ez a szerver a jövőben világsikerré válik (ami elég való színűtlen, figyelembe véve, hogy mennyire primitív), egy állandó, 1024 alatti portot jelölnek majd ki neki, és meg fog jelenni a www.iana.org-on is. A szervei következő két sora két szükséges állandót definiál. Az első az állomány átvitel során használt adatblokkok méretét határozza meg. A második azt adja meg, hogy hány kapcsolat várakozhat egyszerre a portra, mielőtt a szerver eldobná a továb bi beérkező kéréseket. A lokális változók deklarációja után kezdődik a szerver tényleges kódja. A prog ram a szerver IP-címét tartalmazó adatszerkezetek inicializálásával indul. Ezt az adat szerkezetet hamarosan a szerver csatlakozójához kötjük majd. A memset meghívása az egész adatszerkezetet 0-ba állítja, a következő három hozzárendelés pedig kitölti há rom mezőjét. Ezek közül a legutolsó tartalmazza a szerver portját. A htonl és a htons függvények használata azért szükséges, mert az értékeket egy szabványos formára kell alakítanunk annak érdekében, hogy a kód mind a nagy-endián (pl. SPARC), mind a kis-endián (pl. a Pentium) számábrázolást használó processzorokon helyesen fusson. A pontos szemantikájuk itt most nem érdekes. A szerver ezután létrehoz egy csatlakozót és (az Í < 0 feltétellel) ellenőrzi, hogy ez rendben lezajlott-e. A kód termékként kiadott változatában egy hangyányit bőbeszé dűbb hibaüzenetet kellene írni. A setsockopt meghívására azért van szükség, hogy a szerver újrahasználhassa a portot, és így határozatlan ideig futhasson és szolgálhassa ki a beérkező kéréseket. Az IP-címet mostanra hozzákötöttük a csatlakozóhoz, és azt
534
SZÁMÍTÓGÉP-HÁLÓZATOK
is megvizsgáltuk, hogy a bind hívása sikeres volt-e. Az inicializálás végsó' lépése a listen meghívása, amellyel a szerver bejelenti, hogy hajlandó a bejövó' hívások elfoga dására, valamint megbízza a rendszert, hogy QUEUE_SIZE-nyi kérést várakoztasson abban az esetben, ha akkor érkezik új kérés, amikor a szerver éppen egy másik kérés kiszolgálásán dolgozik. Ha a várakozási sor megtelik, és további kérések érkeznek, akkor azokat a rendszer csendben eldobja. Ez az a pillanat, amikor a szerver belép a fő ciklusba, amelyet ezután már el sem hagy. Leállításának egyetlen módja az, ha kívülről lövik ki. Az accept meghívása addig blokkolja a szervert, amíg összeköttetési kérés nem érkezik egy klienstől. Ha az accept hívása sikeres, akkor egy állományleíróval tér vissza, amelyet ezután ahhoz hasonlóan lehet írásra és olvasásra használni, ahogyan egy csővezeték állomány leírójával lehet a csővezetéket írni és olvasni. Az egyirányú csővezetékekkel ellentétben azonban a csatla kozók kétirányúak, így az sa-t (socket address; a csatlakozó címe) egyaránt lehet hasz nálni az összeköttetésről való olvasáshoz, illetve az arra történő íráshoz. Miután a szerver kiépítette az összeköttetést, kiolvassa az sa-bó\ a kért állomány nevét. Ha a név nem áll azonnal rendelkezésre, akkor addig várakozik, amíg meg nem kapja. Mi után a szerver megkapta az állomány nevét, megnyitja az állományt és belép abba a hurok ba, amely addig olvassa ki sorban az állomány darabjait és írja ki ezeket a csatlakozóra, amíg a teljes állományt át nem másolta. Ezután a szerver lezárja az állományt és az össze köttetést, majd várni kezd a következő kapcsolódási kérésre. Ezt a hurkot örökké ismétli. Most vizsgáljuk meg a kliens kódját. A működésének megértéséhez elengedhetet len megérteni azt, hogy hogyan kell indítani. Ha feltesszük, hogy a program neve client, akkor egy tipikus hívása a következő: client flits.cs.vu.nl /usr/tom/allomanynev >f
Ez a hívás csak abban az esetben sikeres, ha a szerver már fut a.flits.cs.vu.nl-en, a /usr/tom/allomanynev nevű állomány létezik, és a szervernek olvasási joga is van rá. Ha a hívás sikeres, akkor a kliensprogram az interneten keresztül megkapja az állo mányt és kiírja/-be, majd ezután kilép. Mivel a szerver egy-egy átvitel után tovább fut, a klienst újra és újra elindíthatjuk, ha más állományokat is meg akarunk szerezni. A kliens kódja néhány függvénykönyvtár betöltésével és néhány deklarációval kezdődik. Az első dolga ellenőrizni azt, hogy megfelelő számú paraméterrel indítot ták-e a programot (az argc = 3 a program nevén kívül még 2 paramétert jelent). Fi gyeljük meg, hogy az argvfl] a szerver nevét tartalmazza (a példában flits.cs.vu.nl), és ezt a gethostbyname használatával alakítjuk IP-címmé. Ez a függvény a DNS szolgá latot használja a cím kikeresésére. A DNS-t a 7. fejezetben fogjuk tanulmányozni. Ezután a kliens egy csatlakozót (socket) hoz létre és inicializálja azt, majd a connect hívással megpróbál létrehozni egy TCP-összeköttetést a szerver felé. Ha a megnevezett gépen fut a szerver, továbbá figyeli a SERVER_PORT-ot, és vagy tétlen, vagy van szabad helye a listen hívás várakozási sorában, akkor az összeköttetés (előbb vagy utóbb) kiépül. A kliens ekkor a csatlakozóra történő írással elküldi az állomány nevét az összeköttetésen keresztül. Az elküldött bájtok száma eggyel nagyobb a név tényleges hosszánál, mivel a nevet lezáró 0 bájtot is el kell küldeni a szervernek, hogy az tudja, hol van vége a névnek.
A SZÁLLÍTÁSI RÉTEG
535
Ekkor a kliens egy olyan hurokba lép, amelyben az állományt blokkonként kiol vassa a csatlakozóról, és rámásolja a blokkokat a standard outputra. Amikor ezzel végzett, egyszerűen kilép.
/*Ezen az oldalon egy olyan kliensprogram található, amely a következő oldalon lát ható szerverprogramtól le tud kérni egy állományt. A szerver a teljes állomány átküldé sével válaszol. 7 #include <sys/types.h> #include <sys/socket.h> #include
#include #define SERVER_PORT 12345 /* tetszőleges, de a két oldalon azonosnak kell lennie */ #define BUF^SIZE 4096 /* átvitt darabok mérete */ int main(int argc, char *argv) { int c, s, bytes; char buf[BUF_SIZE]; /* puffer az érkező állománynak */ struct hostent *h; /* info a szerverről 7 struct sockaddrjn channel; /* ez tárolja majd az IP-címet 7 if (argc !=3) fatalflndítás: client <szervemév> <állománynév>"); h = gethostbyname(argv[1]); /* lekérjük a hoszt IP-címét 7 if (!h) fatalfgethostbyname sikertelen"); s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (s <0) fatal("csatlakozó"); J memset(&channel, 0, sizeof(channel)); channel.sin_family= AFJNET; memcpy(&channel.sin_addr.s.addr, h->h_addr, h->h_length); channel.sin„port=htons(SERVER_PORT); c = connect(s, (struct sockaddr *) &channel, sizeof(channel)); if (c <0) fatal("Az összeköttetés kiépítése sikertelen"); /* Az összeköttetés létrejött. Elküldjük az állomány nevét, 0-val lezárva. 7 write(s, argv[2], strlen(argv[2])+1) /* Kiolvassuk az állományt a csatlakozóról és kiírjuk a strandard outputra. 7 while(1){ bytes = read(s, buf, BUF__SIZE) /* olvasás a csatlakozóról 7 if (bytes <= 0) exit(0); /* vége van az állománynak? 7 write(1, buf, bytes); /* írás a standard outputra 7 } } fatal(char *string) { printf("%s\n", string); exit(1); }
6.6. ábra. A csatlakozókat használó kliens kódja. A szerver kódja a következő oldalon találh
536
SZÁMÍTÓGÉP-HÁLÓZATOK
#include <sys/types.h> #include <sys/fcntl.h> #include <sys/socket.h> #include #include #define SERVER_PORT 12345
/* Ez a szerver kódja */
/* tetszőleges, de a két oldalon azonosnak kell lennie */ /* átvitt darabok mérete */
#define BUF_SIZE 4096 #defineQUEUE_SIZE10 int main(int argc, char *argv[]) { int s, b, I, fd, sa, bytes, on = 1; char buf[BUF_SIZE]; /* puffer a kimenő állománynak 7 struct sockaddrjn channel; /* ez tárolja majd az IP-címet 7 /* Felépítjük a csatlakozóhoz szükséges adatszerkezetet. */ memset(&channel, 0, sizeof(channel)); /* nullázzuk a csatornát */ channel.sin_family = AFJNET; channel. sin_addr.s_addr = htonl(INADDR_ANY); channel.sin_port = htons(SERVER_PORT); /* Passzív megnyitás. Összeköttetésre várunk. */ s = socketíAFJNET, SOCK_STREAM, IPPROTO_TCP); /* csatlakozó létrehozása 7 if (s < 0) fatal("socket sikertelen"); setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)); b = bind(s, (struct sockaddr *) Schannel, sizeof(channel)); if (b < 0) fatal("bind sikertelen"); I = listen(s, QUEUE_SIZE); /* megadjuk a várakozási sor hosszát */ if (I < 0) fatalflisten sikertelen"); /* A csatlakozó kész és be van kötve. Várjuk az összeköttetéseket, és feldolgozzuk azokat. */ while(1){ sa = accept(s, 0, 0); /* várakozás egy összeköttetési kérésre */ if (sa < 0) fatal("accept sikertelen"); read(sa, buf, BUF_SIZE); /* beolvassuk az állománynevet
a csatlakozóról */ /* Megszerezzük és átvisszük az állományt .*/ fd = open(buf, 0_RDONLY); I* megnyitjuk a kért állományt 7 if (fd < 0) fatal("open sikertelen"); while (1){ bytes = read(fd, buf, BUF_SIZE) /* olvasás az állományból 7 if (bytes <= 0) exit(O); /* vége van az állománynak? 7 write(sa, buf, bytes); /* bájtok kiírása a csatlakozóra 7 } close(fd); /* állomány lezárása 7 close(sa); /* összeköttetés lezárása 7 } } 6.6. ábra. Folytatás
A SZÁLLÍTÁSI RÉTEG
537
A fatál függvény kitesz egy hibaüzenetet a kimenetre, majd kilép. A szervernek is szüksége van erre a függvényre, de a hely szűkös volta miatt nem írtuk le kétszer. A klienst és a szervert külön fordítják, és általában más számítógépeken futtatják, ezért nem oszthatják meg & fatál eljárás kódját. Ez a két program (a könyvhöz kapcsolódó többi anyaggal egyetemben) megtalál ható a könyv weboldalán: http://www.prenhall.com/tanenbaum
Itt a borító fényképe melletti „Web Site" hiperhivatkozásra kell kattintania. A prog ramokat bármely UNIX rendszerrel (pl. Solaris, BSD, Linux) le lehet tölteni és le is lehet fordítani a cc-o client client.c -Isocket -Insl cc-o server server.c -Isocket -Insl
parancsok segítségével. A szerver indításához csak ezt kell begépelni: server
A kliens indításához a fent már ismertetett két paraméter szükséges. A weboldalról a programok Windowsos változatai is letölthetők. A rend kedvéért meg kell említenem, hogy ez a szerver nem a legjobb a szerverek között. A hibaellenőrzése kevéssé alapos, a hibajelentései középszerűek, valamint agyafúrt módon sohasem hallott a biztonságról. A csupasz UNIX rendszerhívások használata sem a legjobb eszköz a platform-függetlenség eléréséhez. A program to vábbá él néhány olyan, műszakilag lehetetlen feltételezéssel, mint például az a felté telezés, hogy az állománynév belefér a pufferbe, illetve, hogy automatikusan átvitelre kerül. A szerver teljesítménye gyenge, mert minden kérést szigorúan soros módon ke zel (mivel csak egyetlen szálon fut). Mindezen hiányosságai ellenére azonban egy teljes és működőképes internetes állományszerver. A feladatokban az olvasót is buz dítjuk arra, hogy javítson ezen a két programon. A csatlakozók programozásával kap csolatos további információért lásd (Stevens, 1997) művét.
6.2.
A szállítási protokollok elemei
A szállítási szolgálatot egy, a szállítási entitások között használt szállítási protokoll va lósítja meg. Némely tekintetben a szállítási protokollok az adatkapcsolati protokollok ra emlékeztetnek, amelyeket a 3. fejezetben tanulmányoztuk részletesen. Mindkettő nek többek között hibakezelést, sorszámozást és forgalomszabályozást kell végeznie. Ugyanakkor jelentős eltérések is vannak a kettő között. A különbségek fő oka ab ban az alapvetően eltérő működési környezetben rejlik, melyben a két protokoll mű ködik. Ezt a 6.7. ábrán láthatjuk. Az adatkapcsolati rétegben a két csomópont közvet lenül egy fizikai csatornán keresztül kommunikál, míg a szállítási rétegben a fizikai
538
SZÁMÍTÓGÉP-HÁLÓZATOK
Router
Router
Alhálózat
Fizikai kommunikációs csatorna
(a)
(b)
6.7. ábra. (a) Az adatkapcsolati réteg környezete, (b) Szállítási réteg környezete
csatorna helyett egy egész alhálózat szerepel. Ez a különbség fontos hatással van a protokollokra. Egyrészt az adatkapcsolati rétegben a routernek nem kell kijelölni, hogy melyik másik routerrel kíván kommunikálni - minden kimenő' vonal egyértelműen azonosít egy adott routert. A szállítási rétegben a cél explicit címzése kötelező. Másrészt, amikor egy folyamat a 6.7.(a) ábrán látható vezetéken összeköttetést akar létesíteni, egyszerű dolga van: a másik végpont mindig jelen van (hacsak el nem rom lott, amikor is nincs jelen). Akármelyik eset következik is be, nincs sok tennivaló. A szállítási rétegben, mint látni fogjuk, a kezdeti összeköttetés-létesítés jóval bonyolultabb. Egy másik nagyon bosszantó különbség az adatkapcsolati és a szállítási réteg kö zött az alhálózat potenciális adattároló képessége. Ha egy router elküld egy keretet, az vagy megérkezik, vagy elvész, de nem fog bolyongani egy darabig, elrejtőzni a világ egy távoli sarkába, majd hirtelen, egy váratlan pillanatban, mondjuk 30 másodperc múlva fölbukkanni. Ha az alhálózat a belső forgalmat datagramokkal és adaptív forga lomszabályozással valósítja meg, nem elhanyagolható annak a valószínűsége, hogy a csomagot valamelyik csomópont tárolja pár másodpercig, és csak ezután kézbesíti. Az alhálózat adattároló képességének következménye néha katasztrofális lehet, és spe ciális protokollok használatát teszi szükségessé. Az utolsó különbség az adatkapcsolati és a szállítási réteg között inkább mennyisé gi, mint minőségi eltérés. Pufferelés és forgalomszabályozás mindkét esetben szüksé ges, de a szállítási rétegben jelenlevő nagy és változó számú összeköttetés eltérő meg közelítést igényel, mint amit az adatkapcsolati rétegben használtunk. Néhány, a 3. fe jezetben tárgyalt protokoll rögzített számú puffert rendel minden vonalhoz, így a beér kező keretek számára mindig van szabad puffer. A szállítási rétegben kezelendő nagy számú összeköttetés láttán máris kevésbé vonzó ötlet mindegyiknek számos saját puf fert lefoglalni. A következő alfejezetekben többek között ezekkel a fontos problémák kal fogunk foglalkozni.
6.2.1.
Címzés
Amikor egy alkalmazási folyamat (vagyis egy felhasználó) egy távoli alkalmazási fo lyamattal akar összeköttetést létrehozni, meg kell jelölnie, hogy melyik folyamattal akar kapcsolatba lépni. (Az összeköttetés nélküli szállítási szolgálatban is megvan ugyanez a probléma: Kinek kell elküldeni az egyes üzeneteket?) Az általánosan hasz-
539
A SZÁLLÍTÁSI RÉTEG
2. hoszt
1. hoszt Alkalmazási folyamat O
1208-as
J>/TSAP
1.szerver
f Szállítási réteg
Szállítási összeköttetés! ^NSAP
2. szerver
Alkalmazási réteg
A
\
1522-es \ TSAP \f
1836-os TSAP
Hálózati réteg
NSAP
Adatkapcsolati réteg Fizikai réteg
6.8. ábra. A TSAP-k, az NSAP-k és a szállítási összeköttetések. nált módszer az, hogy külön szállítási címeket definiálunk az egyes folyamatok részé re, amelyeken várhatják az összeköttetési kéréseket. Az Interneten ezeket a végponto kat általában portoknak hívják. Az ATM hálózatokban AAL-SAP a nevük. Mi a leg általánosabb kifejezést, a TSAP-t (Transport Service Access Point - szállítási szolgálatelérési pont) fogjuk használni. Az ezekkel rokon végpontokat a hálózati ré tegben (vagyis a hálózati rétegbeli címeket) ugyanígy NSAP-nak (Network SAP hálózati szolgálatelérési pont) hívják. Az IP-címek például NSAP-k. A 6.8. ábra az NSAP, a TSAP és a szállítási összeköttetés összefüggéseit mutatja be. Az alkalmazási folyamatoknak, mind a kliens, mind a szervergépen egy helyi TSAP-re kell rákapcsolódniuk ahhoz, hogy egy távoli TSAP-vel összeköttetést tudja nak létrehozni. Ezek az összeköttetések az ábrán is látható módon mindkét hoszt NSAP-jén is keresztülhaladnak. Egyes hálózatokban minden számítógép csak egyet len NSAP-vel rendelkezik, így szükség van egy olyan módszerre, amellyel több szál lítási végpontot lehet megkülönböztetni egy NSAP-n belül. Erre a célra alkalmazzák a TSAP-ket. A szállítási összeköttetés egy lehetséges forgatókönyve: 1. A 2. hoszton található pontos idő folyamat az 1522-es TSAP-hez kapcsolódik és bejövő" hívásokra várakozik. Az, hogy egy folyamat hogyan tud egy TSAP-re rá kapcsolódni, teljesen kívül esik a hálózat modelljén és kizárólag a helyi operációs rendszertől függ. A kapcsolódás történhet például egy a mi LiSTEN-ünkhöz hasonló hívással. 2. Az 1. hoszt egyik alkalmazási folyamata meg akarja tudni a pontos időt, ezért egy
540
SZÁMÍTÓGÉP-HÁLÓZATOK
CONNECT hívásban megjelöli forrásként az 1208-as TSAP-t és célként az 1522-es TSAP-t. Ez végül az 1. hoszt alkalmazási folyamata és a 2. hoszt 1. szervere kö zötti összeköttetés kiépítéséhez vezet. 3. Az alkalmazási folyamat ezután elküldi a pontos időre vonatkozó kérését. 4. Az időszerver-folyamat a pontos idővel válaszol neki. 5. A szállítási összeköttetést ezután lebontják. Eközben persze a 2. hoszton más szerverek is várhatnak bejövő összeköttetési ké réseket. Ezek más TSAP-kre csatlakoznak, de az ezek számára érkező kérések is ugyanazon az NSAP-n keresztül érkeznek meg. Az itt lefestett kép nagyszerű, azonban egyetlen apró kérdést elegánsan a szőnyeg alá söpörtünk: Honnan tudja az 1. hoszt alkalmazási folyamata, hogy a pontosidő szerver az 1522-es NSAP-hez kapcsolódik? Az egyik lehetőség az, hogy az időszerver már évek óta az 1522-es NSAP-hez csatlakozik, és ezt szép lassan a hálózat minden felhasználója megtanulta. Ebben a modellben a szolgálatoknak stabil TSAP-címük van, amelyeket közismert helyeken megtalálható állományokban sorolnak fel. Ilyen például a UNIX rendszerek /etc/services állománya, amely felsorolja, hogy mely szer verek mely portokhoz vannak állandó jelleggel hozzárendelve. Habár a stabil TSAP-címek jól működnek kisszámú olyan szolgálat esetén, ame lyek sohasem változnak (pl. webszerverek), a felhasználói folyamatok (általános ér telemben) legtöbbször más olyan felhasználói folyamatokkal akarnak beszélgetni, amelyek csak rövid ideig léteznek, és nem rendelkeznek előre ismert TSAP-címmel. Ha mindezen felül sok olyan szerverfolyamat áll rendelkezésre a rendszerben, ame lyeket csak ritkán használnak, akkor pazarló megoldás, ha mindegyik egész nap fut és egy stabil TSAP-címet figyel. Összefoglalva: jobb megoldásra van szükség. Egy ilyen megoldás egyszerűsített formája látható a 6.9. ábrán, amely kezdeti öszszeköttetés-protokollként (initial connection protocol) ismert. Ahelyett, hogy az összes lehetséges szerver egy jólismert TSAP-t figyelne, minden olyan gépnek van egy különleges folyamatszervere (process server), amely szolgálatokat akar felkí nálni a távoli felhasználóknak. A folyamatszerver a kevésbé sűrűn használt szerverek megbízottjaként működik. Több portot figyel egyszerre, összeköttetési kérésekre vár va. A szolgáltatásokat használni kívánók azzal kezdik a tevékenységüket, hogy kiad nak egy CONNECT kérést, amelyben megjelölik, hogy melyik TSAP-címen van az ál taluk igényelt szolgáltatás. Ha itt nem vár rájuk szerver, akkor a folyamatszerverrel kerülnek kapcsolatba, ahogyan az a 6.9.(a) ábrán is látható. Miután a folyamatszolgáltató megkapja a beérkező kérést, létrehozza a megfelelő szervert, aminek átadja a felhasználóval már fennálló összeköttetését. A szerver elvég zi a kért feladatot, miközben a folyamatszolgáltató továbbra is kérésekre várakozik. Ezt mutatja be a 6.9.(b) ábra. Míg a kezdeti összeköttetést létesítő protokoll ragyogóan működik olyan szerverek esetén, melyeket elegendő akkor létrehozni, amikor szükség van rájuk, sok eset van, amikor szolgáltatások a folyamatszolgáltatótól függetlenül léteznek. Például egy álló-
541
A SZÁLLÍTÁSI RÉTEG
1. hoszt
2. hoszt
1. hoszt
2. hoszt Pontos idő ^szerver.
Folyamatszolgáltatóy
(a)
'Folyamat.szolgáltatóy
(b)
6.9. ábra. Az 1. koszton futó felhasználói folyamat és a 2. gépen futó pontos idő szerver közötti összeköttetés felépítése
mányszolgáltatónak speciális hardveren (nagykapacitású merevlemezzel ellátott gépen) kell futnia, nem lehet csak úgy, menetközben létrehozni, amikor valaki használni akarja. Az ilyen esetek kezelésére gyakran egy alternatív módszert alkalmaznak. Ebben a modellben egy névszolgáltatónak (name server) vagy olykor katalógusszolgáltató nak (directory server) is nevezett speciális folyamat működik. Hogy a felhasználó egy adott szolgáltatás nevéhez (pl. „pontos idő") tartozó TSAP-címet megtudja, össze köttetést létesít a névszolgáltatóval (ami a jól ismert TSAP-címén várakozik). A fel használó üzenetet küld a kért szolgáltatás nevével, mire a névszolgáltató visszaküldi annak TSAP-címét. A felhasználó ezután megszünteti az összeköttetést a névszolgál tatóval, és újat létesít a kívánt szolgáltatással. Ebben a modellben egy új szolgáltatás létesítésekor a szolgáltatás nevével (rend szerint egy ASCII füzér) és TSAP-címével be kell jelentkezni a névszolgáltatónál, s az a kapott információt feljegyzi belső' adatbázisába. Ha később kérés érkezik erre a szolgáltatásra, tudni fogja a választ. A névszolgáltató feladata analóg a tudakozóéval a távbeszélőrendszerekben - ne vekről számokra történő leképezést valósít meg. Éppúgy, mint a távbeszélőrendsze rekben, lényeges, hogy a névszolgáltató (vagy kezdeti összeköttetést létesítő protokoll esetén a folyamatszolgáltató) jól ismert TSAP-címe valóban mindenki által ismert le gyen. Ha nem tudjuk a tudakozó telefonszámát, nem lehet fölhívni a tudakozót, hogy megkérdezzük. Ha azt gondolnánk, hogy a tudakozó telefonszáma nyilvánvaló, pró báljuk meg valamikor fölhívni egy másik ország tudakozóját.
542 6.2.2.
SZÁMÍTÓGÉP-HÁLÓZATOK
Összeköttetés létesítése
Az összeköttetés felépítése egyszerűnek tűnik, de valójában meglepően trükkös folya mat. Első pillantásra elegendő lenne, hogy az egyik szállítási entitás CONNECTION REQUEST TPDU-t küldene a másik félnek, és CONNECTION ACCEPT (ÖSSZEKÖTTETÉS ELFOGADVA) válaszra várna. A probléma akkor merül fel, ha a hálózat csomagokat veszít, tárol, vagy akár megkettőz. Ezek a lehetőségek komoly nehézségeket okoznak. Képzeljük el, hogy egy alhálózat annyira zsúfolt, hogy a nyugtázások nemigen ér keznek vissza időben, minden csomag időtúllépéssel érkezik meg, a csomagokat két szer-háromszor újraküldik. Tegyük föl, hogy az alhálózat belül datagramokat használ, és minden csomag különböző útvonalon halad. Néhány csomag közlekedési dugóba kerülhet, és hosszú ideig nem érkezik meg, vagyis az alhálózat jelentős ideig tárolja őket, majd jóval később bukkannak elő. A létező legrosszabb rémálom a következő: egy felhasználó összeköttetést létesít egy bankkal, és üzenetet küld azzal a megbízással, hogy a bank utaljon át nagy pénz összeget egy nem igazán megbízható személy számlájára, majd lebontja az összeköt tetést. Szerencsétlenségére minden elküldött csomag megkettőződik, és valahol a há lózat mélyén tárolódik. Az összeköttetés befejezése után ezek sorban előbukkannak az alhálózatból, és helyes sorrendben megérkeznek a bankhoz újabb összeköttetést és tranzakciót kérve, majd ez az összeköttetés is megszűnik. A bank nem tudhatja, hogy kettőzött üzenetekről van szó. Feltételezi, hogy ez egy független, második tranzakció, így ismét átutalja a pénzt. Jelen alfejezetben a késleltetett kettőzések problémáját fog juk tanulmányozni. Különös figyelmet szentelünk az összeköttetések megbízható mó don történő létesítésére kifejlesztett algoritmusokra, hogy a föntiekhez hasonló rémál mok ne válhassanak valóra. A probléma alapvető oka a késleltetett kettőzések létezése. Többféle ellenszer léte zik, de egyik sem teljesen kielégítő. Az egyik módszer azt mondja, hogy használjunk eldobható szállítási címeket. Ebben a megközelítésben minden esetben, amikor egy szállítási címre van szükség, újat generálunk. Az összeköttetés lebontása után a régi címet elvetjük, és soha nem használjuk újra. Ez a stratégia a 6.9. ábrán látható folyamatszolgáltató-modell működését lehetetlenné teszi. Egy másik lehetőség minden összeköttetésnek egy olyan egyedi összeköttetés-azo nosítót adni (egy sorszámot, ami minden újabb összeköttetés létesítésekor eggyel nő), amit a kezdeményező fél generál és minden TPDU-ba (beleértve az összeköttetést ké rőt is) beletesz. Az összeköttetés lebontása után minden szállítási entitás frissíti a befe jeződött összeköttetések tábláját, amelynek bejegyzései (társ szállítási entitás, össze köttetés sorszám) párokból állnak. Minden újabb összeköttetés-kéréskor ellenőrizhető, hogy az nem egy régi összeköttetéshez tartozik-e. Sajnos ennek a módszernek van egy alapvető hibája: minden szállítási entitásnak határozatlan ideig történeti információt kell tárolnia. Ha egy gép összeomlik, és me móriatartalmát elveszti, többé nem tudja, hogy mely összeköttetés-azonosítót használ ta már. Ehelyett más megközelítést kell vennünk. Ahelyett, hogy egy csomagot örök időre életben hagynánk az alhálózatban, ki kell fejlesztenünk egy olyan mechanizmust, amely az öreg és még mindig bolyongó csomagokat kiirtja. Ha garantálni tudjuk, hogy
A SZÁLLÍTÁSI RÉTEG
543
egyetlen csomag sem él tovább egy adott idó'tartamnál, a probléma valamivel kezelhe tőbbé válik. A csomagok élettartama ismert maximumra korlátozható az alábbi módszerek kö zül valamelyikkel: 1. Korlátozott alhálózat tervezése. 2. Átugrásszámláló (hop counter) alkalmazása a csomagokban. 3. A csomagok időbélyeggel való ellátása. Az első módszerbe minden olyan megoldás beletartozik, amely megelőzi, hogy a csomagok hurokba kerüljenek, és emellett valahogyan a torlódási késleltetést is kor látozni tudja a (pillanatnyilag ismert) leghosszabb lehetséges útvonalon. A második módszer abból áll, hogy az átugrásszámot valamilyen alkalmas értékre állítják be, és minden továbbadás alkalmával csökkentik. A hálózati protokoll minden olyan keretet egyszerűen eldob, amelynek az átugrásszámlál ója nullára csökkent. A harmadik mód szerhez arra van szükség, hogy minden csomagot ellássanak a keletkezésének idejé vel, valamint a routereknek meg kell egyezniük abban, hogy eldobnak minden olyan csomagot, amely régebbi a közösen meghatározott legnagyobb lehetséges élettartam nál. Ez utóbbi módszer alkalmazásához a routerek óráit szinkronizálni kell, amely maga sem egyszerű feladat, hacsak nem a hálózaton kívül valósítják meg a szinkroni zálást. Erre használhatunk például GPS-t vagy egy olyan rádióadót, amely periodiku san adatszórással közli a pontos időt. Gyakorlatban nem elég azt biztosítanunk, hogy egy csomag halott, hanem ennek igaznak kell lenni minden rá vonatkozó nyugtára is, ezért bevezetjük a T időtartamot, ami a valódi maximális csomagélettartam kis egész számú többszöröse. Az alkalma zott szorzó protokollfüggő, és szerepe egyszerűen csak T növelése. A csomag elküldé sét követően T idő várakozás után biztosak lehetünk abban, hogy a csomag már min den nyom nélkül eltűnt, és sem a csomag, sem a nyugtázások nem fognak hirtelen előtűnni a ködből, és további bonyodalmakat okozni. Korlátozott élettartamú csomagokat felhasználva bolondbiztos eljárást lehet kifej leszteni az összeköttetés biztonságos felépítésére. Az alább ismertetett eljárás Tomlinson nevéhez fűződik (1975). Ezzel ugyan megoldódik a probléma, viszont egyéb gondok jelentkeznek. A módszert továbbfinomította Sunshine és Dalai (1978), ennek különböző változatait széles körben alkalmazzák a gyakorlatban. Annak a problémának megkerülésére, hogy egy gép egy összeomlás után nem tud ja megállapítani, hogy hol is tartott, Tomlinson azt javasolta, hogy minden hosztot időt mutató órával lássanak el. A különböző hosztokon levő óráknak nem szükséges szinkronban járniuk. Minden óra egy bináris számlálóval valósítható meg, ami egysé ges időközönként növeli értékét. Ezenkívül a számlálóban levő bitek számának egyen lőnek vagy nagyobbnak kell lennie, mint a sorszámokban levő bitek száma. Végül, ami a legfontosabb, a hoszt meghibásodásakor is tovább kell járnia az órának. Az alapötlet az, hogy az algoritmus nem engedi két azonos sorszámú TPDU egy idejű létezését. Az összeköttetés felépítésekor az óra értékének alsó k bitje alkotja a
544
SZÁMÍTÓGÉP-HÁLÓZATOK
120 E N
52 o co
0
0
30
Összeomlás után 70-es sorszámmal újrakezdés _l I 60 90 120 150 180 Idő (a)
használt sorszámok Idő (b)
6.10. ábra. (a) TPDU nem kerülhet a tiltott tartományba, (b) Az újraszinkronizációs probléma
kezdeti (szintén k bites) sorszámot. így, eltérően a 3. fejezetben leírt protokolloktól, minden összeköttetés más sorszámmal kezdi számozni a TPDU-it. A használt tarto mánynak olyan nagynak kell lennie, hogy mire a sorszámok körbeérnek, az azonos sorszámú TPDU már rég eltűnjön. Ezt az időt és a kezdeti sorszámok közti lineáris összefüggést mutatja a 6.10. ábra. Ha valamikor a szállítási entitások már megegyeztek a kezdeti sorszámban, bármi lyen csúszóablakos protokoll használható forgalomszabályozásra. Valóságban a kez deti sorszám időfüggését jelző vastag görbe nem igazán egyenes, hanem lépcsőzött, mivel az óra értéke diszkrét lépésekben növekszik. Az egyszerűség kedvéért ezt a részletet elhanyagoljuk. Probléma akkor lép föl, ha egy hoszt összeomlik. Újraindulásakor a benne működő szállítási entitás nem fogja tudni, hogy milyen sorszámnál tartott. Az egyik lehetséges megoldás szerint, a szállítási entitások újraindulás után várjanak T ideig, hogy az öszszes régi TPDU addigra eltűnhessen. Egy összetett, több hálózatot összekapcsoló há lózatban azonban T igen nagy lehet, így ez a stratégia kevésbé vonzó. Egy összeomlás utáni T időtartamnyi tétlenség elkerülésére, a sorszámokra beveze tünk egy másik korlátozást is. Ennek szükségességét legkönnyebben egy példán ke resztül mutathatjuk be. Legyen a maximális csomagélettartam, T, pontosan 1 perc. Az óra másodpercenként növeli értékét. Amint azt a 6.10.(a) ábrán látható vastag vonal is mutatja, az x időpontban fölépített összeköttetés kezdeti sorszáma x lesz. Tegyük föl, hogy t = 30 másodperckor egy közönséges adat TPDU indul a (már korábban létreho zott) 5-ös összeköttetésen keresztül 80-as sorszámmal, legyen ez az X TPDU. Köz vetlen elküldése után a hoszt összeomlik, majd gyorsan újraindul. t= 60 pillanatban megkezdi 0-tól 4-ig terjedő sorszámú összeköttetéseit újra fölépíteni, t - 70-kor az 5ös összeköttetést is újra létrehozza 70-es kezdeti sorszámmal, ahogy az elő van írva. A következő 15 másodperc alatt 11 TPDU-t küld el 70-től 80-ig terjedő sorszámokkal, így / = 85 másodperckor egy új, 80-as sorszámú TPDU indul el az 5-ös összekötteté sen keresztül az alhálózatban. Sajnos azonban az X TPDU még mindig létezik. Ha ez
545
A SZÁLLÍTÁSI RÉTEG
az új, 80-as számú TPDU előtt érkezik meg, a vevő az X TPDU-t fogadná el, és az igazi, 80-as számút mint kettőzést eldobná. Az ilyen problémák megelőzésére el kell kerülnünk a sorszámok használatát (azaz új TPDU-hoz rendelését) a potenciális kezdeti sorszámként történő alkalmazás előtti T időtartamban. Az illegális (sorszám-idő) párosítást a 6.10. ábrán látható tiltott tarto mány jelöli. Bármely TPDU bármely összeköttetésen történő továbbítása előtt a szál lítási entitásnak le kell olvasnia az óráját, hogy ellenőrizze, nem a tiltott tartományban van-e. A protokoll így is kétféleképpen kerülhet bajba. Ha egy hoszt túl gyorsan küld túl sok adatot egy frissen létesített összeköttetésen, az aktuális sorszám-idő görbe merehoszt
1. hoszt
2. hoszt Régi kettőzött
•^7**/™
^
(<*<*. y) (b)
hoszt
1. hoszt
/D/"7snr Régi kettőzött (tyű,'9ta:
•y)
(c) 6.11. ábra. Három forgatókönyv a háromutas kézfogás protokollal történő összeköttetés létesítésre, CR és CA rendre CONNECTION REQUEST és CONNECTION ACCEPTED rövidítései,
(a) Normális működés, (b) Régi kettőzött CR bukkan elő. (c) Kettőzött CR és kettőzött adat TPDU esete
546
SZÁMÍTÓGÉP-HÁLÓZATOK
dekebben emelkedhet, mint a kezdeti sorszám-idő görbe. Ez azt jelenti, hogy a maxi mális adatsebesség bármelyik összeköttetésen egyenlő egy TPDU-val óraütésenként. Azt is jelenti, hogy egy összeomlás utáni újraindításkor egy új összeköttetés megnyi tása előtt a szállítási entitásnak egy óraütésig kell várni, hogy elkerülje egy sorszám ismételt használatát. Mindkét probléma rövidebb óraütem (pár ezredmásodperc) hasz nálatát teszi indokolttá. Sajnos, nemcsak úgy lehet bajba kerülni, hogy túl gyors adással alulról kerül a szállítási entitás a tiltott tartományba. A 6.10.(b) ábrán látszik, hogy tetszőleges, az óra sebességénél kisebb adási sebességnél a tényleges idő-sorszám görbe végül balról fog belépni a tiltott tartományba. Minél meredekebb az említett görbe, annál később következik be ez az esemény. Mint fönt említettük, minden TPDU elküldése előtt a szállítási entitásnak meg kell vizsgálnia, hogy belépne-e a tiltott tartományba, és ha igen, vagy vár Ts-ot az elküldés előtt, vagy újraszinkronizálja a sorszámokat. Az óra alapú módszer megoldja az adat TPDU-k késleltetett kettőzési problémáit, de hogy ez a módszer használható legyen, először létre kell hozni az összeköttetést. Mivel a vezérlő TPDU-k szintén késhetnek, a két fél potenciális problémája az, hogy hogyan egyezzenek meg a kezdeti sorszámban. Tegyük fel például, hogy az összeköt tetés úgy épül föl, hogy az 1. hoszt elküldi a CONNECTION REQUEST TPDU-t a hasz nálni kívánt kezdeti sor- és portszámmal a távoli 2. hosztnak. Ez utóbbi nyugtázza a kérést egy CONNECTION ACCEPTED TPDU visszaküldésével. Ha a CONNECTION REQUEST TPDU elvész, de később fölbukkan egy kettőzött példánya a 2. hosztnál, az összeköttetés helytelenül jön létre. Ennek a problémának megoldására vezette be Tomlinson a háromutas kézfogás (three-way handshake) módszert (1975). Ez az összeköttetés-létesítési protokoll nem igényli, hogy mindkét fél azonos sorszámmal kezdje az adást, így a globális időtől el térő módszer is használható szinkronizálásra. Egy normális összeköttetés-létesítési el járást, amikor az 1. hoszt kezdeményez, láthatunk a 6.11.(a) ábrán. Az 1. hoszt kivá laszt egy x sorszámot, és egy CONNECTION REQUEST TPDU-ban elküldi a 2. hosztnak. Az egy CONNECTION ACCEPTED TPDU-val nyugtázza x értékét, és bejelenti saját y kezdeti sorszámát. Végül az 1. hoszt jóváhagyja a 2. hoszt által választott kezdeti sor számot az első általa küldött adat TPDU-ban. Lássuk tehát, hogy működik a háromutas kézfogás protokollja késleltetett kettőzött vezérlő TPDU-k esetén. A 6.11.(b) ábrán az első TPDU egy régebbi összeköttetés késleltetett kettőzött CONNECTION REQUEST üzenete. Ez a 2. hoszthoz anélkül érkezik meg, hogy az 1. hoszt tudna róla. A 2. hoszt válaszul CONNECTION ACCEPTED TPDU-t küld egy kéréssel bővítve, hogy ellenőrizze, partnere tényleg új összeköttetést akar-e létesíteni. Mivel az 1. hoszt elutasító választ küld, a 2. hoszt rájön, hogy egy késlelte tett kettőzés csapta be, és felhagy az összeköttetés-létesítéssel. Ily módon egy késlelte tett kettőzött TPDU nem okoz kárt. A legrosszabb eset az, amikor mind egy megkésett CONNECTION REQUEST, mind egy CONNECTION ACCEPT kering még valahol a hálózatban. Ezt az esetet mutatja a 6.11.(c) ábra. Az előző példához hasonlóan a 2. hoszt itt is egy megkésett CONNEC TION REQUEST-et kap, amelyre válaszol. Itt nagyon fontos azt figyelembe venni, hogy a 2. hoszt kezdetben az y-t javasolta a 2. hoszttól az 1. hoszt felé menő forgalom sor számának, annak a ténynek a teljesen biztos tudatában, hogy olyan TPDU már nem
547
A SZÁLLÍTÁSI RÉTEG
létezik, amely az y sorszámot tartalmazza, és ezekre vonatkozó nyugták sincsenek már úton. Amikor a második késve érkező' TPDU megérkezik a 2. hoszthoz, az a tény, hogy ez a z-t nyugtázza és nem az v-t, elárulja a 2. hoszt számára, hogy ez is egy régi kettőzött TPDU. A dolog legfontosabb tanulsága az, hogy a régi TPDU-k semmilyen kombinációja sem okozhatja a protokoll elromlását, vagyis sosem hozhat létre olyan összeköttetéseket véletlenül, amelyeket senki nem kért.
6.2.3.
Az összeköttetés bontása
Az összeköttetést bontani jóval egyszerűbb, mint felépíteni. Azonban több buktató van itt, mint gondolnánk. Mint korábban említettük, az összeköttetés kétféleképpen bontható: aszimmetrikus és szimmetrikus módon. Aszimmetrikus bontást alkalmaznak pl. a távbeszélő-hálózatokban: amikor az egyik fél leteszi a kagylót, az összeköttetés megszakad. Szimmetrikus bontás esetén az összeköttetést két független egyirányú összeköttetésként kezelik, ahol mindkettőt külön kell lebontani. Az aszimmetrikus összeköttetés-bontás váratlanul történik és adatvesztéssel járhat. Vegyük például a 6.12. ábra forgatókönyvét. Az összeköttetés létrejötte után az 1. hoszt egy TPDU-t küld a 2. hosztnak, s az rendben meg is érkezik. Az 1. hoszt újabb TPDU-t küld. Sajnos a 2. hoszt kiad egy DlSCONNECT-et mielőtt a második TPDU megérkezik. Az összeköttetés lebomlik, és ezzel együtt az adat elvész. Világos, hogy kifinomultabb bontási protokoll szükséges az adatvesztés elkerülésé hez. Egyfajta megoldás a szimmetrikus bontás használata, amikor is mindkét irányt a másiktól függetlenül bontjuk le. Itt egy hoszt azután is fogadhat adatot, hogy már el küldött egy DISCONNECT REQUEST TPDU-t. A szimmetrikus bontás megfelelően működik, ha mindkét félnek rögzített mennyi ségű elküldendő adata van, és mindegyik pontosan tudja; hogy mikor küldte el. Más helyzetekben annak eldöntése, hogy végeztek dolgukkal, és a kapcsolat bontható, nem 1. hoszt
2. hoszt
Nincs adattovábbítás összeköttetés-bontás kérése után 6.12. ábra. Hirtelen összeköttetés-bontás adatvesztéssel
548
SZÁMÍTÓGÉP-HÁLÓZATOK
annyira nyilvánvaló. Elképzelhető egy olyan protokoll, melyben az 1. hoszt így szól: „Végeztem. Te is készen vagy?" „Én is elkészültem. Szervusz!" - válaszol a 2. hoszt, és az összeköttetés biztonságosan lebontható. Sajnos, ez a protokoll nem mindig működik. Egy híres, a két-hadsereg probléma néven ismert feladat pont erről szól. Képzeljük el, hogy a fehér hadsereg a völgyben táborozik, mint a 6.13. ábra mutatja. Mindkét környező dombot a kék hadsereg birto kolja. A fehér sereg bármelyik kék hadseregnél nagyobb, azonban azok együtt na gyobbak a fehér seregnél. Ha bármelyik kék hadsereg egyedül támad, biztos vereséget szenved, de együttes támadásuk során megsemmisíthetnék a fehér sereget. A kék seregek össze akarják egyeztetni támadásukat. Azonban az összes kommuni kációs lehetőségük a völgyön gyalog átküldött futárokra korlátozódik, akiket persze elfoghatnak, így az üzenet elvész (tehát megbízhatatlan kommunikációs csatornát használnak). Az a kérdés, hogy létezik-e olyan protokoll, ami győzelemre segíti a kék seregeket? Tegyük fel, hogy az 1. kék hadsereg parancsnoka a következő üzenetet küldi: „Azt javaslom, hogy március 29-én támadjunk. Mi a véleményetek?" Tegyük fel továbbá, hogy az üzenet megérkezik, a 2. kék hadsereg parancsnoka egyetért, és válasza sze rencsésen megérkezik az 1. kék sereghez. Végrehajtják a támadást? Valószínűleg nem, mert a 2. sereg parancsnoka nem tudja, hogy üzenete átjutott-e a völgyön. Ha nem, az 1. sereg nem fog támadni, így egymaga bolond lenne fölvenni a küzdelmet. Bővítsük hát a protokollt a háromutas kézfogás technikával. Az eredeti javaslat kezdeményezőjének nyugtáznia kell a kapott választ. Feltéve, hogy nem veszett el üzenet, a 2. kék hadsereg megkapja a nyugtát, de most az 1. kék sereg parancsnoka fog habozni. Végül is ő nem tudja, hogy a nyugta átjutott-e vagy sem, és ha nem, tud ja, hogy nem számíthat a 2. sereg támadására. Használhatnánk négyutas kézfogást is, de az sem segítene. Valójában könnyen bebizonyítható, hogy nem lehet működő protokollt létrehozni. Tegyük fel mégis, hogy van ilyen. A protokoll utolsó üzenete vagy lényeges, vagy nem. Utóbbi esetben hagyjuk el az összes többi fölösleges üzenettel együtt, amíg olyan protokollhoz nem jutunk, melynek minden üzenete nélkülözhetetlen. Mi törté2. kék hadsereg
6.13. ábra. A két-hadsereg probléma
A SZÁLLÍTÁSI RÉTEG
549
nik, ha az utolsó üzenet nem jut át? Mivel erről megállapítottuk, hogy lényeges, így ha elvész, nem kezdődik el a támadás. Mivel az utolsó üzenet küldője sohasem lehet biz tos annak célba érkezésében, nem kockáztatja meg a támadást. Sőt, ami még rosszabb, ezt a másik kék sereg is tudja, így ők sem támadnak. Hogy észrevegyük a két-hadsereg probléma és az összeköttetések bontása között vonható párhuzamot, helyettesítsük a „támadás" szót a „bontás"-sal. Ha egyik fél sem készült föl a bontásra addig, míg meg nem győződött arról, hogy partnere is fölké szült, az összeköttetés bontására sohasem kerül sor. A gyakorlatban a felek több kockázatot vállalnak az összeköttetés lebontásánál, mint ha a fehér hadsereget kellene megtámadniuk, ezért a helyzet nem teljesen re ménytelen. A 6.14. ábrán négy forgatókönyvet mutatunk az összeköttetés-bontásra há romutas kézfogás használata esetén. Bár ez a protokoll nem sebezhetetlen, mégis ki elégítően viselkedik. A 6.14.(a) ábrán a normális működést láthatjuk, ahol az egyik partner az összeköt tetés bontásának kezdeményezéseként DR (DISCONNECTION REQUEST) TPDU-t küld és elindít egy időzítőt. Amikor ez megérkezik, a vevő szintén visszaküld egy DR TPDU-t, és elindít egy időzítőt arra az esetre, ha az általa küldött DR elveszne. Amikor ez a DR megérkezik, a kezdeményező visszaküld egy ACK TPDU-t, és bontja az összeköttetést. Végül, mikor az ACK TPDU megérkezik, a vevő szintén bontja az összeköttetést. Az összeköttetés bontása azt jelenti, hogy a szállítási entitás az összeköttetésre vonatkozó információt törli a nyitott kapcsolatok táblájából, és jelzi az összeköttetés befejezését a tulajdonosnak (a szállítási felhasználónak). Ez a művelet eltér attól, amikor a szállí tási felhasználó kiad egy DISCONNECT primitív hívást. Ha az utolsó ACK TPDU elvész, ahogy az a 6.14.(b) ábrán látható, a helyzetet az időzítő menti meg. Amikor az lejár, az összeköttetés mindenképpen befejeződik. Most tekintsük azt az esetet, amikor a második DR vész el. Az összeköttetés bontá sát kezdeményező fél nem kapja meg a várt választ, lejár az időzítője, és az egészet el kezdi elölről. A 6.14.(c) ábrán látható ez a működés, feltételezve, hogy a második pró bálkozás során nem vesznek el a TPDU-k, és mindegyik időben meg is érkezik. Az utolsó eset, amit a 6.14.(d) ábrán láthatunk, azonos az előzővel, kivéve, hogy most feltételezésünk szerint minden további kísérlet a DR újraküldésére meghiúsul a TPDU-k elvesztése következtében. N próbálkozás után a küldő föladja, és fölbontja az összeköt tetést. Eközben a vevő időzítése szintén lejár, és ő is bontja az összeköttetést. Bár ez a protokoll rendszerint sikeresen működik, elméletileg kudarcot vallhat, ha a kezdeti DR és a további N újraküldött TPDU mind elvesznek. A küldő feladja és bont ja az összeköttetést, míg a másik fél semmit sem tud a bontási kísérletekről, és még mindig teljesen aktív. Ennek a helyzetnek az eredménye egy félig nyitott összeköttetés. Ez a probléma elkerülhető lenne, ha nem hagynánk, hogy a küldő N próbálkozás után föladja a kísérletezést, hanem kényszerítenénk, hogy örökké folytassa, amíg vá laszt nem kap. Ha viszont a másik fél időzítést figyel, és az lejár, a küldő ténylegesen örökké fog próbálkozni, mert többé nem is kaphat választ. Ha nem engedjük a fogadó oldalt, hogy időtúllépés esetén a várakozást feladja, a 6.14.(d) ábrán látható protokoll elakad. Egyik módja a félig nyitott összeköttetések kilövésének a következő: bevezetünk egy olyan szabályt, miszerint ha adott ideig nem érkezik TPDU, az összeköttetést au-
550
SZÁMÍTÓGÉP-HÁLÓZATOK
tomatikusan bontjuk. Ily módon, ha valamelyikük befejezi az összeköttetést, a másik észreveszi a forgalom hiányát, és szintén bontja az összeköttetést. Természetesen ezen szabályozás bevezetése szükségessé teszi egy időzítő alkalmazását minden szállítási entitásnál, amit az minden TPDU elküldésekor nulláz és újraindít. Ha lejár, akkor egy üres (adatot nem hordozó) TPDU-t küld, hogy visszatartsa a vevőt az összeköttetés bontásától. Ha azonban az automatikus lebontási szabályt alkalmazzuk, és túl sok egy más után küldött üres TPDU elvész az amúgy kihasználatlan összeköttetésen, először az egyik, majd a másik oldal is bontja az összeköttetést. 2. hoszt
1. hoszt DR elküldése és időzítő indítása
DR elküldése és időzítő indítása
Összeköttetés bontása ACK küldése
1. hoszt
DR elküldése - _ D R ^ és időzítő indítása DR elküldése és időzítő indítása Összeköttetés bontása
ACK küldése
-4cv
Összeköttetés bontása
(a)
1. hoszt
2. hoszt
szett<
DR elküldése és időzítő indítása
Összeköttetés bontása ACK küldése
~^£íl^ (c)
ACK ^Elve<szett
^(Időtúllépés) összeköttetés bontása
(b)
DR elküldése és időzítő - ^ D f i ^ DR elküldése indítása W&&és időzítő indítása Elve< (Időtúllépés^ DR elküldésed és időzítő indítása
2. hoszt
Összeköttetés bontása
1. hoszt
2. hoszt
DR elküldése és időzítő indítását^ • -eEhett>
—-2ÍL
DR elküldése és időzítő indítása
(Időtúllépés) DR elküldése ~ ~ " - | E I V és időzítő *CSZÍ ^ indítása . (N-szer időtúllépés) összeköttetés bontása
(Időtúllépés) összeköttetés bontása (d)
6.14. ábra. Négy protokoll forgatókönyv az összeköttetés lebontására. (a) Normális működés a háromutas kézfogással, (b) Az ACK vész el. (c) A válasz vész el. (d) A válasz és a rákövetkező DR-ek vesznek el
A SZÁLLÍTÁSI RÉTEG
551
Nem ragozzuk tovább ezt a kérdést, de mostanra már az olvasó bizonyára megér tette, hogy egy összeköttetés adatvesztés nélküli lebontása közel sem annyira egysze rű, mint amilyennek az az első ránézésre tűnik.
6.2.4.
Forgalomszabályozás és pufferelés
Az összeköttetés-létesítés és -lebontás többé-kevésbé részletes tárgyalása után vizsgál juk meg, hogyan kezelik az összeköttetéseket használat közben. Már korábban is fel merült az egyik kulcskérdés, a forgalomszabályozás. Bizonyos tekintetben a szállítási réteg forgalomszabályozással kapcsolatos problémái azonosak az adatkapcsolati réteg ben tapasztaltakkal, de vannak eltérések is. Az alapvető hasonlóság az, hogy mindkét esetben csúszóablakos, vagy más módszer alkalmazása szükséges minden összekötte tésre, hogy a lassú vevőt megvédhessük a túl gyors adó üzenettömegétől. A fő különbség az, hogy egy routernek viszonylag kevés vonala van, míg egy hoszt számos összeköt tetéssel rendelkezhet. Ez az eltérés az oka annak, hogy a szállítási rétegben gazdaság talan volna az adatkapcsolati rétegben használt pufferelési eljárást megvalósítani. A 3. fejezetben tárgyalt adatkapcsolati protokollok esetében a kereteket mind a kül dő, mind a fogadó csomópont pufferelte. Például a 6. protokollban mind az adónak, mind a vevőnek MAX__SEQ + 1 puffert kell rendelnie minden vonalához, felét beme netre, felét kimenetre használva. Egy, mondjuk legfeljebb 64 összeköttetéssel rendel kező hoszt és négybites sorszámok használata esetén ez a protokoll 1024 puffert igé nyelne. Az adatkapcsolati rétegben a küldő oldalnak pufferelnie kell a kimenő kereteket, mert azokat esetleg újra kell küldenie. Ha az alhálózat datagram szolgálatot nyújt, a küldő szállítási entitásnak hasonló okból szintén puffert kell használnia. Ha a vevő tudja, hogy a küldő minden TPDU-t tárol addig, míg nyugtát nem kap rájuk, a vevő tetszés szerint rendelhet külön puffert különböző összeköttetéseihez, ahogy azt jónak látja. Esetleg egyetlen közös pufferterületet tarthat fenn az összes összeköttetése ré szére. Amikor egy újabb TPDU érkezik, megpróbál dinamikusan puffert igényelni. Ha sikerrel jár, elfogadja a TPDU-t, kudarc esetén eldobja. Mivel a küldő kész újraküldeni az alhálózatban elveszett TPDU-kat, nem probléma, ha a vevő eldob TPDU-kat, bár ezzel erőforrást pocsékol. A küldő addig próbálkozik, míg végül nyugtát nem kap. Összefoglalva, megbízhatatlan hálózati szolgálat esetén a küldő minden kimenő TPDU-t pufferelni kényszerül éppen úgy, mint az adatkapcsolati rétegben. Megbízha tó hálózati szolgálat esetén viszont további kompromisszumok lehetségesek. Ha a kül dő biztos abban, hogy a vevőnek mindig van szabad puffere, nem szükséges a kimenő TPDU-król másolatot fönntartania. Ha azonban a vevő nem ad garanciát, hogy min den beérkező TPDU-t elfogad, az adónak mindenképpen pufferelnie kell. Ez utóbbi esetben a küldő nem bízhat a hálózati réteg nyugtájában, mivel az csak azt jelenti, hogy a TPDU megérkezett, arról nem ad választ, hogy elfogadták-e. Később még visszatérünk erre a fontos kérdésre. Ha megállapodás született is arról, hogy a vevő pufferelni fog, még mindig kérdéses a puffer mérete. Ha a legtöbb TPDU nagyjából azonos méretű, kézenfekvő a pufferterü letet azonos méretű pufferek készletébe szervezni úgy, hogy egy TPDU-ra egy puffer
SZA (TÓGÉP-HÁLÓZATOK
552 _
1 1.TPDU
^ 2 . TPDU
'j
3. TPDU
* (a)
(b) MTPDU Szabad terület
(c)
6.15. ábra. Pufferkezelés. (a) Láncolt, rögzített méretű pufferek. (b) Láncolt, változó méretű pufferek. (c) Összeköttetésenként egyetlen nagy körpuffer jusson, mint azt a 6.15.(a) ábra mutatja. Ha azonban a TPDU-k mérete széles határok közt változhat egy terminálon begépelt pár betűtől az állományátvitelkor mozgatott több ezer karakterig, a rögzített méretű pufferek problémát jelentenek. Ha a puf ferméretet a leghosszabb előforduló TPDU méretében állapítjuk meg, rövid TPDU vé tele esetén a hely nagy része kihasználatlan marad. Ha a pufferméret kisebb a TPDU maximális hosszánál, egy nagy TPDU több puffert és bonyolultabb kezelést igényel. A pufferméret problémájának másik kezelési módja a változó méretű pufferek használata, mint azt a 6.15.(b) ábra mutatja. Ennek előnye a jobb memóriakihasznált ság, ami viszont bonyolultabb pufferkezeléssel jár. A harmadik lehetőség összekötteté senként egyetlen nagy körpuffer alkalmazása, mint az a 6.15.(c) ábrán látható. Ez a meg oldás szintén jó memóriakihasználtságot nyújt akkor, ha az összeköttetések erősen ter heltek, de ez jelentősen romlik, ha néhány összeköttetés gyér forgalmat bonyolít le. A forrásnál és a célnál folyó pufferelés közötti optimális egyensúly függ az össze köttetésen lebonyolított forgalom típusától. Kis sávszélességű, löketszerű forgalom céljára, amilyet pl. egy interaktív terminál állít elő, legjobb, ha sehol se különítünk el puffert, hanem szükség esetén dinamikusan igényeljük mindkét félnél. Mivel az adó nem tudhatja biztosan, hogy a vevő megkapta az igényelt puffert, a nyugta beérkezé séig tárolnia kell az elküldött TPDU-t. Másrészről, állománytovábbítás és más, nagy sávszélességű átvitel esetén jobb, ha a vevő egy teljes ablaknyi puffert rendel a bejövő forgalomhoz, hogy teljes sebességgel folyhasson az adatátvitel. Kis sávszélességű, lö ketszerű forgalom esetén tehát célszerű a küldőnél megvalósítani a pufferelést, míg egyenletes, nagy sávszélességű adatfolyamot a vételi oldalon előnyösebb pufferelni. Ahogy összeköttetések létrejönnek és lebomlanak, és ahogy a forgalom jellege vál tozik, úgy kell az adónak és a vevőnek dinamikusan hozzáigazítani ehhez a pufferfog-
553
A SZÁLLÍTÁSI RÉTEG
lalást. Eszerint a szállítási protokollnak lehetővé kell tennie a küldő entitás számára, hogy a másik féltől pufferterület lefoglalását kérje. Puffereket összeköttetésenként vagy a két hoszt között élő minden összeköttetésre együtt lehet lefoglalni. Alternatív megoldásként a vevő a jövendő forgalmat nem, de saját puffereinek adatait ismerve közölheti az adóval: „Lefoglaltam részedre X db puffert." Ha az éppen használt összeköttetések száma növekedne, szükség lehet a pufferek számára lefoglalt terület csökkentésére. A protokollnak ezt a lehetőséget is biztosíta nia kell. A pufferek dinamikus kezelésének egy ésszerű, általános módja a pufferelésnek a nyugtázástól való különválasztása. Ez a 3. fejezetben leírt csúszóablakos protokollal ellentétes működést jelent. A dinamikus pufferkezelés valójában változó méretű csúszóablakot jelent. Először az adó a forgalom becsült igénye alapján puffert kér a vevő től. A vevő annyi puffert ad, amennyit adhat. Az adó minden TPDU küldésekor ezt a számot csökkenti, és leáll az átvitellel, ha eléri a nullát. A vevő közben a visszafelé menő forgalomra ülteti a nyugtákat és a pufferek foglaltsági jellemzőit. A 6.16. ábrán egy példát mutatunk arra, hogy hogyan működne a dinamikus ablak kezelés datagram alhálózat fölött négybites sorszámokkal. Tegyük fel, hogy a pufferfoglaltsági információ - mint az ábra jelzi - nem az ellentétes irányba közleke dő TPDU-kra ültetve, hanem külön TPDU-ban utazik. Először az A hoszt 8 puffert Üzenet
B
Megjegyzés
1 -» < 8 puffer kérése >
-> A 8 puffert szeretne
2 «- < nyűg = 15, puff = 4 >
«- B csak 0-3 sorszámú üzeneteket engedélyezi
3 -*
< sorsz = 0, adat = mO > -» A-nak 3 puffere maradt
4 -> < sorsz = 1, adat = m1>
•* A-nak 2 puffere maradt
5 -» < sorsz = 2, adat = m2 >
.. Az üzenet elveszett, de A azt hiszi, hogy már csak egy puffere maradt
6 «- < nyűg = 1, puff = 3 >
*- B nyugtázza 0 és 1 sorszámúakat, 2-4-et engedélyezi
7 -» < sorsz = 3, adat = m3 >
•* A-nak 1 puffere maradt
8 -» < sorsz = 4, adat = m4 >
*
A-nak nincs több puffere, le kell állnia
9 -» < sorsz = 2, adat = m2 >
*
A időzítése lejár és újraküldi a 2-es üzenetet
10 <-
< nyűg = 4, puff = 0 >
s- Minden nyugta megérkezett, de A még mindig blokkolt
11
< nyűg = 4, puff = 1 >
•*- A most küldheti az 5-ös üzenetet
-
12 «- < nyűg = 4, puff = 2 >
*- B valahol talált egy újabb puffert
13 -» < sorsz = 5, adat = m5 > 14 -» < sorsz = 6, adat = m6 >
* *
A most ismét blokkolódik
15 «- < nyűg = 6, puff = 0 > 16 ... < nyűg = 6, puff = 4 >
-
A továbbra sem küldhet
A-nak 1 puffere maradt
i- Potenciális holtpont
6.16. ábra. Dinamikus pufférfoglalás. i nyilak az átvitel irányát mutatják, a^hárompont(...) elveszett TPDU-t jelöl
554
SZÁMÍTÓGÉP HÁLÓZATOK
szeretne, de ezekből csak négyet kaphat. A ezután elküld három TPDU-t, melyek kö zül az utolsó elvész. A 6. TPDU nyugtáz az l-es sorszámúval bezárólag (azt is bele értve) minden elküldött TPDU-t, lehetővé téve A-nak, hogy azok puffereit fölszabadít sa. Ezenfelül arról is értesíti A-t, hogy újabb három TPDU-t küldhet l-es fölötti sor számmal (tehát a 2-es, 3-as és 4-est). A tudja, hogy a kettes sorszámút már továbbítot ta, úgy gondolja, hogy elküldheti a rákövetkező kettőt, és így is tesz. Ezen a ponton blokkolódik, és további pufferek lefoglalására kell várakoznia. Viszont az időtúllépés miatti újraküldés blokkolt állapotban is lehetséges (9-es sor), mivel ilyenkor az elkül dendő TPDU már pufferben van. A 10-es sorban B nyugtázza minden TPDU meg érkezését a 4-es sorszámúval bezárólag (azt is beleértve), de nem hagyja A-t tovább adni. Ez a helyzet nem fordulhat elő a 3. fejezetben tárgyalt rögzített ablakméretű protokolloknál. A következő B által küldött TPDU újabb puffert foglal, így A tovább haladhat. Ilyen jellegű pufferfoglalási módszernél problémát jelenthet, ha a szállítási réteg datagram alhálózatra épül, és egy vezérlő TPDU vész el. Nézzük a l ó . sort! B újabb puffereket foglalt le A részére, de a TPDU, amivel erről A-t tájékoztatná, elveszett. Mivel a vezérlő TPDU-k nem kapnak sorszámot, és időzítő se figyeli hiányukat, az A holt pontba kerül. Ezen helyzet elkerülésére mindkét hoszt rendszeres időközönként vezér lő TPDU-kat küld társának, amelyekben nyugta és az összeköttetésekhez tartozó puf ferek állapotáról szóló információ van. Ily módon előbb-utóbb föloldódik a holtpont. Eddig hallgatólagosan feltételeztük, hogy a küldő adási sebességének egyedül a vevő szabad puffereinek száma szab határt. A memóriaárak rohamos csökkenése miatt viszont lehetséges annyi memóriát zsúfolni a hosztokba, hogy a szabad pufferek hiá nya ritkán vagy egyáltalán nem okoz problémát. Amikor a pufferterület többé nem korlátozza a maximális forgalmat, egy újabb szűk keresztmetszet bukkan föl: a hálózat szállítókapacitása. Ha szomszédos routerek legfeljebb x keret/s sebességre képesek, és k független út van két hoszt között, semmi lyen módon nem valósíthat meg a szóban forgó két hoszt kx TPDU/s-nál nagyobb se bességű átvitelt, akármennyi szabad puffere van is a két félnek. Ha a küldő túl sűrűn ad (tehát kx-né\ több TPDU-t küld másodpercenként), az alhálózatban torlódás kelet kezik, mert nem képes olyan gyorsan továbbítani a TPDU-kat, mint amilyen gyorsan kapja azokat. Valójában olyan mechanizmusra van szükség, amely az alhálózat szállítókapacitá sára, és nem a vevő pufferfoglalási lehetőségeire épít. Világos, hogy a forgalomszabá lyozási algoritmust a küldőnél kell megvalósítani, nehogy túl sok általa küldött TPDU nyugtázatlan maradjon. Belsnes (1975) egy olyan csúszóablakos forgalomszabályozá si módszert javasolt, melyben a küldő dinamikusan a hálózat szállítási kapacitásához igazítja az ablak méretét. Ha a hálózat másodpercenként c TPDU átvitelére képes, és a ciklusidő r (ami magában foglalja a küldési, átviteli és vevő várakozási soraiban eltöl tött időt, a vevő által végzett feldolgozás idejét és a nyugta visszaérkezésének idejét), a küldő ablakmérete cr. Ekkora ablakmérettel normális állapotban a küldő folyama tosan tele csővel dolgozik, így a hálózati teljesítőképesség legkisebb csökkenése is a küldő blokkolását okozza. Az ablakméret rendszeres beállításához a küldőnek mindkét paramétert folyamato san figyelnie kell, és abból az ablak méretét újra kell számolnia. A hálózat szállítási
555
A SZÁLLÍTÁSI RÉTEG
kapacitása egyszerűen meghatározható úgy, hogy adott időtartamig számolja a beérke ző nyugták számát, majd ezt az időintervallum hosszával elosztja. A mérés során a küldőnek olyan gyorsan kell adnia, amilyen gyorsan csak bír, hogy ne a kis adási se besség, hanem a hálózat teljesítőképessége jelentse a fölső korlátot a beérkező nyug ták számára. Az éppen elküldött TPDU-ra beérkező nyugta késleltetését pontosan mérni lehet, és a mérési adatok átlagát az adó folyamatosan számolhatja. Mivel a há lózat kapacitása függ az éppen zajló forgalomtól, az ablakméretet gyakran pontosítani kell, hogy jól kövesse a hálózat teljesítményének ingadozását. Mint később látni fog juk, az Internet hasonló mechanizmust alkalmaz.
Nyalábolás
6.2.5.
Több beszélgetés összeköttetésekre, virtuális áramkörökre és fizikai összeköttetésekre való nyalábolása, multiplexelése a hálózati architektúra számos rétegében játszik sze repet. A szállítási rétegben a multiplexelés igénye többféle okból is felmerülhet. Ha például egy hoszton csak egyetlen hálózati cím áll rendelkezésre, akkor azon a gépen minden szállítási összeköttetésnek azt kell használnia. Szükség van valamilyen mód szerre, amellyel eldönthetjük, hogy a beérkező TPDU-kat melyik folyamatnak kell to vábbítani. Ezt a feladatot feltöltési multiplexelésnek (upward multiplexing) hívják és a 6.17.(a) ábra mutatja be. Az ábrán négy különböző szállítási összeköttetés hasz nálja ugyanazt a hálózati összeköttetést (vagyis IP-címet) a távoli hoszt eléréséhez. A multiplexelés a szállítási rétegben egy másik ok miatt is hasznos lehet. Tegyük fel például, hogy egy alhálózaton belül virtuális áramköröket alkalmazunk, és mind egyiken korlátozzuk az adatsebességet. Ha egy felhasználónak nagyobb sávszélesség re van szüksége, mint amennyit egyetlen virtuális áramkör nyújt, akkor több hálózati összeköttetést kell megnyitnia, és ezeken körforgásos alapon elosztani a forgalmat.
Réteg I 1 4
''
•
J*
Szállítási címek /
1
Hálózati címek
3
2
Router vonalak
1 Router fele
(a)
(b)
6.17. ábra. Nyalábolási módok, (a) Felfelé nyalábolás. (b) Lefelé nyalábolás
556
SZÁMÍTÓGÉP-HÁLÓZATOK
Ezt a 6.17.(b) ábrán is látható működési módot letöltési multiplexelésnek (downward multiplexing) nevezik. Ha k hálózati összeköttetést nyitunk meg, akkor a ren delkezésre álló sávszélesség fc-szorosára növekszik. A letöltési multiplexelés egyik közismert példája az otthoni ISDN-felhasználók esete. Az ISDN-vonal két különálló, egyenként 64 kb/s-os összeköttetést biztosít. Ha mindkettőt arra használjuk, hogy egy Internet-szolgáltatót hívjunk fel és megosztjuk a forgalmat a két vonal között, az lehetó'vé teszi, hogy 128 kb/s-os tényleges sávszélességet érjünk el.
6.2.6.
Összeomlás utáni helyreállítás
Ha a routerek és hosztok összeomlás veszélyének vannak kitéve, fontos kérdéssé válik a feléledés megvalósítása. Ha a teljes szállítási entitás a hoszton belül van megvalósít va, a hálózat és a routerek összeomlás utáni helyreállítása nyilvánvaló. Ha a hálózati réteg datagram szolgálatot nyújt, a szállítási entitások mindig számolnak elveszett TPDU-kkal, és tudják, hogyan kezeljék azokat. Ha a hálózati réteg összeköttetés alapú szolgálatot biztosít, egy megszakadt virtuális áramkör kezelése úgy történhet, hogy a szállítási entitás egy újat hoz létre, és azon megpróbálja megtudni távoli társától, hogy mely TPDU-kat kapta meg, és melyek vesztek el. Ez utóbbiakat aztán újraküldi. Ennél sokkal komolyabb probléma a hosztok összeomlás utáni újraindítása. Általá nos cél lehet, hogy a kliensek a szerver összeomlása és gyors föléledése után tovább tudják folytatni munkájukat. A nehézség illusztrálására tegyük föl, hogy az egyik hoszt, a kliens, egy nagy állományt küld a másik hoszt, az állományszolgáltató számá ra egyszerű megáll-és-vár (stop-and-wait) protokollal. A szerver szállítási rétege egy szerűen egyenként átadja a beérkező TPDU-kat a szállítási felhasználónak. Az átvitel közepén a szerver összeomlik. Amikor újraéled, az összes táblázatát újrainicializálja, így nem tudja, hogy hol tartott. Előző állapotának kiderítése érdekében a szerver minden kliens részére elküldhet egy TPDU-t, melyben bejelenti, hogy tönkrement, és mindenkit kér, hogy küldjék el használt összeköttetéseik állapotát. Minden kliens az alábbi két állapot egyikében le het: egy elküldött TPDU van függőben (SÍ), vagy nincs ilyen TPDU (SO). Csupán ezen információ birtokában el kell döntenie a kliensnek, hogy újraküldje-e a legutolsó TPDU-t, vagy sem. Első pillantásra nyilvánvalónak tűnhet, hogy a kliensnek, amikor megtudja, hogy a szerver összeomlott, csak akkor kell újraküldenie a kérdéses TPDU-t, ha az nyugtázatlan (azaz ha SÍ állapotban van). Közelebbi vizsgálatok során azonban kiderül ennek a naiv megközelítésnek a veszélye. Vegyük például azt a helyzetet, amikor a szerver szállítási entitása elküldi a nyugtát, és miután a nyugta elindult, csak ezután adja át a TPDU-t az alkalmazói folyamatnak. A TPDU kimenő folyamba írása és a nyugta el küldése két, külön-külön oszthatatlan esemény, melyeket nem lehet egyszerre végre hajtani. Ha az összeomlás a nyugta elküldése után, de még a TPDU átadása előtt tör ténik meg, a kliens megkapja a nyugtát, és így SO állapotban lesz, amikor az újrain dulás bejelentése megérkezik. A kliens ekkor nem fogja újraküldeni a TPDU-t, mivel (helytelenül) abban a tudatban él, hogy az megérkezett. Döntése egy hiányzó TPDU-t eredményez.
557
A SZÁLLÍTÁSI RÉTEG
Ezen a ponton azt gondolhatjuk: „Ez a probléma könnyen megoldható. Csak annyit kell tenni, hogy átírjuk a szállítási entitást, és ezentúl eló'ször a folyamba ír, és csak azután küldi a nyugtát." Játsszuk el a fönti kísérletet ismét! Képzeljük el, hogy a fo lyamba írás már megtörtént, de az összeomlás éppen a nyugta elküldése eló'tt következik be. A kliens így SÍ állapotban lesz, és újraküldi a TPDU-t, ami így észrevétlenül egy második TPDU-ként a szerveroldali alkalmazási folyamat irányába menő kimeneti adat áramba kerül. Mindegy, hogyan programozzuk a szervert és a klienst, mindig vannak olyan hely zetek, melyekben ez a protokoll kudarcot vall. A szerver kétféleképpen valósítható meg: eló'ször nyugtáz vagy először ír. A kliens négyféleképpen programozható: min dig újraküldi az utolsó TPDU-t, sohasem küldi újra, csak az SO állapotban küldi újra, vagy csak az 57-ben. Ez nyolc kombinációt jelent, de mint látni fogjuk, ezek közül mindegyikhez van egy eseményhalmaz, aminek hatására a protokoll hibázik. A szerver oldalán három esemény lehetséges: nyugta küldése (Ny), kimeneti folyam ba írás (K) és összeomlás (Ö). A három esemény hat különböző sorrendben történhet: NyÖ(K), NyKÖ, Ö(NyK), Ö(KNy), KNyÖ és KÖ(Ny), ahol a zárójelek azt jelölik, hogy az összeomlást már sem nyugtázás, sem írás nem követi (azaz ha a rendszer összeom lott, akkor tényleg összeomlott). A 6.18. ábrán a kliens- és szerverstratégiák nyolc kombinációját mutatjuk be az érvényes eseménysorrendekkel együtt. Vegyük észre, hogy minden stratégiára található valamilyen eseménysorrend, amire a protokoll ku darcot vall. Például, ha a kliens újraküld, az NyKÖ eseménysorozat észrevétlen kettő zést eredményez, miközben a másik két sorozatra helyesen működik a protokoll. A protokoll további finomítása sem segít. Még ha a szerver és a kliens - mielőtt a szerver írni próbálna - több TPDU-t váltana is egymással, hogy a kliens pontosan tudja, mi fog történni, arról semmiképpen sem szerezhet tudomást, hogy az összeom lás közvetlenül az írás előtt vagy utána közvetlen következett-e be. A következtetés elkerülhetetlen: alapszabályunk - mely szerint az események nem történhetnek párhu zamosan - kizárja annak lehetőségét, hogy a rendszerösszeomlás és újraindulás a felső rétegek számára transzparens módon mehessen végbe. A vevő hoszt (szerver) stratégiája Az adó hoszt (kliens) stratégiája Mindig újraküld Soha nem ad újra
Először nyugta, aztán kiírás
Először kiírás, aztán nyugta
NyÖ(K)
NyKÖ
Ö(NyK)
Ö(KNy)
OK
KETTŐZ
OK
OK
VESZT
OK
VESZT
VESZT
SOban újraküld
OK
Sí-ben újraküld
VESZT
KETTŐZ VESZT
OK
KNyÖ
OK
VESZT KETTŐZ
OK
OK
OK = a protokoll helyesen működik KETTŐZ = a protokoll üzenetet kettőz VESZT = a protokoll üzenetet veszít 6.18. ábra. A kliens- és szerverstratégia különböző kombinációi
KÖ(Ny)
KETTŐZ KETTŐZ
OK
OK OK KETTŐZ
558
SZÁMÍTÓGÉP-HÁLÓZATOK
Általánosabban szólva, ez az eredmény azt jelenti, hogy az N. réteg összeomlásából történő újraindulás csak az N + 1. réteg segítségével lehetséges, feltéve, hogy a maga sabb réteg elegendő információt tárol az alatta levő állapotáról. Mint fönt említettük, a szállítási réteg akkor képes kezelni a hálózati rétegben történt összeomlásokat, ha az összeköttetés mindkét vége állandóan nyomon követi, hogy hol tartanak. Ez a probléma végül elvezet ahhoz a kérdéshez, hogy mit is jelent valójában az ún. végpontok közötti nyugtázás. Alapjában véve a szállítási protokoll két végpont között működik, és nem láncolt, mint az alatta levő rétegek. Most vegyük azt az esetet, ami kor a felhasználó a távoli adatbázisban tranzakciókat kezdeményez. Tegyük föl, hogy a távoli szállítási entitás programja szerint először átadja a TPDU-t a fölötte levő ré tegnek, és ezután nyugtáz. Még ebben az esetben sem jelenti a nyugta vétele a felhasz náló gépén, hogy a távoli hoszt addig működőképes maradt, amíg a tranzakció lezárá sa meg nem történt. Egy igazi végpontok közötti nyugtát - melynek vétele azt jelenti, hogy a tevékenység ténylegesen végbement, és hiánya annak elmaradását jelzi - való színűleg lehetetlen elérni. A kérdésről bővebben Saltzer és munkatársai (1984) művé ben olvashatunk.
6.3.
Egyszerű szállítási protokoll
Ebben a szakaszban egy bemutató jellegű szállítási réteget fogunk megvizsgálni, hogy az imént megtárgyalt elveket a gyakorlathoz is hozzákapcsoljuk. A példánk absztrakt primitívjei a 6.2. ábra összeköttetés alapú primitívjei lesznek. Az összeköttetés alapú primitívek alkalmazása miatt a példa hasonló a TCP-hez (de természetesen egysze rűbb annál). 6.3.1.
A példa szolgálati primitívjei
Első gondunk az, hogy miképpen írhatjuk le pontosan ezeket a szállítási primitíveket. A CONNECT esete egyszerű: lesz egy connect könyvtári eljárásunk, ami az összeköt tetés felépítéséhez a megfelelő paraméterekkel meghívható. Paraméterei a helyi és tá voli TSAP-k (szállítási szolgálat elérési pontok, azaz szállítási címek). A rutin végre hajtása közben, mialatt a szállítási entitás összeköttetést próbál létesíteni, a hívó blokkolódik (azaz futása fölfüggesztődik). Ha az összeköttetés sikeresen felépült, a hívó blokkolása megszűnik, és elkezdheti az adatátvitelt. Amikor egy folyamat bejövő hívásokra akar várakozni, a figyelni kívánt TSAPcímmel meghívja a listen eljárást. A folyamat ezután addig blokkolva marad, amíg egy távoli folyamat összeköttetést nem próbál létesíteni ehhez a TSAP-hez. Megjegyezzük, hogy ez a modell erősen aszimmetrikus. Az egyik oldal passzív, egy listen végrehajtása után vár, amíg nem történik valami. A másik fél aktív, az kez deményezi az összeköttetést. Felmerül egy érdekes kérdés: mi a teendő, ha az aktív fél kezdi előbb a tevékenységét. Az egyik stratégia szerint a próbálkozás összeköttetés lé tesítésére kudarcot vall, ha a távoli TSAP-n nem figyel senki. Egy másik megközelí tésben viszont a hívó is blokkolódik (akár örökre), amíg egy hallgató fel nem tűnik.
559
A SZÁLLÍTÁSI RÉTEG
A példánkban használt megoldás kompromisszum a két lehetőség között. A fogadó oldal az összeköttetés-kérést adott ideig fenntartja. Ha azon a hoszton egy folyamat üs tén hívást hajt végre, mielőtt az időzítő lejár, az összeköttetés létrejön. Ellenkező esetben a fogadó oldal a kérést elutasítja, a hívó blokkolása megszűnik, és hiba-visszajelzést kap. Az összeköttetés bontásához a disconnect eljárást fogjuk alkalmazni. Miután mind két fél bekapcsolódott, az összeköttetés lebomlik, más szóval szimmetrikus összeköt tetés-bontási modellt használunk. Az adatátvitellel pontosan ugyanaz a probléma, mint az összeköttetés felépítésével: az adat elküldése aktív tevékenység, de a vétel passzív folyamat. Ugyanazt a megol dást fogjuk használni adatátvitelre, mint összeköttetés-létesítésre. Megvalósítunk egy aktív send hívást, ami továbbítja az adatot, és egy passzív receive hívást, ami a vevőt egy TPDU beérkezéséig blokkolja. Konkrét szolgálatdefiníciónk így öt primitívből áll: CONNECT, LISTEN, DISCON NECT, SEND és RECEIVE. Mindegyik primitív pontosan annak a könyvtári rutinnak felel meg, amely végrehajtja azt. A szolgálati primitívek és a könyvtári eljárások paraméte rei a következők: össz_szám össz_szám státus státus státus
= = = = =
LISTEN(helyi) CONNECT(helyi, távoli) SEND(össz_szám, puffer, bájtszám) RECEIVE(össz_szám, puffer, bájtszám) DISCONNECT(össz_szám)
A LISTEN primitív bejelenti a hívó szándékát összeköttetés elfogadására a megadott TSAP-re. A primitív addig blokkolja használóját, amíg nem érkezik összeköttetés-lé tesítési kérés. Nincs időzítés. A CONNECT primitív két paramétert vár, a helyi-ben egy helyi TSAP-t (szállítási címet) és távoli-ban egy távoli címet. A két TSAP között megpróbál összeköttetést lé tesíteni. Ha sikeres, egy nemnegatív összjszám értékkel tér vissza, ami a további hívá sok során az összeköttetést azonosítja. Sikertelen kísérlet esetén a kudarc okát negatív össz_szám értékkel jelzi a primitív. Az általunk használt egyszerű modellben minden TSAP csak egyetlen összeköttetésben szerepelhet, így a hiba oka az is lehet, hogy az egyik TSAP már foglalt. Emellett hibát okozhat a távoli gép üzemzavara, érvénytelen helyi vagy távoli cím használata. A SEND primitív egy, vagy szükség esetén akár számos üzenet formájában küldi el a puffer tartalmát a megjelölt szállítási összeköttetésen. Az esetlegesen előforduló hi bákról a status-ban lehet hibaértesítést visszaküldeni a feladónak. A hiba háromféle lehet: nincs összeköttetés, a puffer címe nem érvényes vagy a számláló értéke negatív. A RECEIVE primitív jelzi a hívó adatvételi szándékát. A beérkező üzenet méretét a bájtszám paraméterben kell megadni. Ha a távoli folyamat már lebontotta az összeköt tetést, vagy érvénytelen a puffereim (pl. az alkalmazói programon kívüli területre mu tat), a státus visszatérési értéke a probléma okát jelző hibakód lesz. A DISCONNECT primitív befejezi az össz_szám paraméterrel jelzett szállítási össze köttetést. Lehetséges hibák: össz_szám egy másik folyamathoz tartozik vagy érvénytelen az összeköttetés-azonosító. A státus visszatérési értéke a hibakód vagy siker esetén 0.
560 6.3.2.
SZÁMÍTÓGÉP-HÁLÓZATOK
A példa szállítási entitása
Mielőtt rátérnénk a példa szállítási entitás programkódjának tárgyalására, gondoljuk végig, hogy az itt bemutatott példa analóg a 3. fejezetben látott korai példákkal: sok kal inkább pedagógiai, mint komoly felhasználói célokat szolgál. Az egyszerűség ked véért sok, valódi rendszerekben nélkülözhetetlen technikai részletet (mint pl. a kimerí tő hibakezelést) kihagytunk belőle. A szállítási réteg a hálózati szolgálati primitívek segítségével küld és fogad TPDUkat. Ki kell tehát választanunk a példában használt hálózati szolgálati primitíveket. Egy lehetséges választás a megbízhatatlan datagram szolgálat, de hogy egyszerű ma radjon a példa, nem ezt használjuk. A megbízhatatlan datagram szolgálat használata esetén a szállítási primitívek kódja nagy és összetett lenne, mert leginkább elveszett és késleltetett csomagokkal kellene bajlódnia. Emellett ezeket a módszereket már részle tesen megvizsgáltuk a 3. fejezetben. Ehelyett összeköttetés alapú megbízható hálózati szolgálatot fogunk használni. Ez zel a választással olyan, a szállítási rétegben felmerülő témákra tudunk összpontosíta ni, melyek nem fordulnak elő az alsóbb rétegekben. Többek között idetartozik az öszszeköttetés felépítése és lebontása, és a kreditek kezelése. Példánk leginkább az ATM hálózatra épülő egyszerű szállítási szolgálathoz fog hasonlítani. A szállítási entitás általában a hoszt operációs rendszerének része is lehet, vagy egy olyan, önálló függvénykönyvtár, amely a felhasználó címterében fut. A mi példánk az egyszerűség kedvéért úgy készült, mintha egy függvénykönyvtár volna, de ennek el lenére nagyon kevés változtatással az operációs rendszer részévé tehető (a változtatá sok elsősorban a felhasználói pufferek elérését érintik). Érdemes azonban megjegyeznünk, hogy ebben a példában a „szállítási entitás" egyáltalán nem különálló funkcionális elem, hanem a felhasználói folyamat része. így, amikor a felhasználó egy blokkoló primitívet (mint például a LISTEN) hajt végre, a tel jes szállítási entitás szintén blokkolódik. Amíg ez a tervezés megfelelő egy egyetlen felhasználói folyamatot tartalmazó hoszt esetén, többfelhasználós hoszt esetén termé szetesebb lenne, ha a szállítási entitás a felhasználói folyamatoktól független külön processzként lenne megvalósítva. A hálózati réteg felé kialakított interfészt a to_net és &from_net eljárások képezik. Mindkettőnek hat paramétere van. Az első az összeköttetés azonosítója, amely egy egytípusú leképezést végez a hálózati virtuális áramkörökre. Ezt követik a Q és M bitek, melyek egybe állítása vezérlőüzenetet, illetve az adott üzenet további csomagjait jelö li. Ezután a csomag típusa következik, ami a 6.19. ábrán látható hat csomagtípus közül az egyik lehet. Végül az adat kezdetét jelző mutató és az adatbájtok száma szerepel. A szállítási entitás a tojnet hívás előtt az összes paramétert kitölti a hálózati réteg számára, hogy az olvassa ki a.from_net hívás során a paramétereket a beérkező cso magból a szállítási entitás számára. Az információt eljárás paramétereiben adjuk át a hálózati rétegnek, és nem magát az elküldött vagy beérkező csomagot, így a szállítási rétegnek nem kell ismernie a hálózati protokoll részleteit. Ha a szállítási entitás akkor próbálna elküldeni egy csomagot, amikor az alatta levő virtuális áramkör csúszóablaka tele van, a to_net eljárással együtt blokkolódik, amíg szabad hely nem lesz az ab lakban. Ez a mechanizmus a szállítási entitás számára teljesen átlátszó, vezérlését -
561
A SZÁLLÍTÁSI RÉTEG
Jelentés
Hálózati csomag CALL REQUEST
Összeköttetés-létesítés kérése
CALL ACCEPTED
Válasz CALL REQUEST csomagra
CLEAR REQUEST
Összeköttetés-bontás kérése
CLEAR CONFIRMATION
Válasz CLEAR REQUEST csomagra
DATA
Adatcsomag
CREDIT
Vezérlőcsomag az ablakkezeléshez
6.19. ábra. A példánkban használt hálózati csomagok enable_transport_layer és disablejransportjlayer jellegű parancsokkal - a hálózati réteg biztosítja a 3. fejezetben leírt protokollokhoz hasonló módon. A csomag csúszóablakának kezelését szintén a hálózati réteg végzi. A fent említett transzparensblokkoló mechanizmuson kívül explicit sleep és wakeup eljárásokat is (melyeket itt nem részletezünk) használ a szállítási entitás. A sleep rutint akkor hívja a szállítási entitás, amikor külső eseményre - rendszerint egy cso mag érkezésére - várva logikailag blokkolódik. A sleep hívás után a szállítási entitás (és vele együtt természetesen a felhasználói folyamat is) felfüggesztődik. A szállítási entitás tényleges kódját a 6.20. ábrán láthatjuk. Minden összeköttetés mindenkor az alábbi hét állapot valamelyikében van: 1. IDLE - Összeköttetés még nem létesült. 2. WAITING -
CONNECT
eljárás lefutott, és a CALL REQUEST csomagot elküldte.
3. QUEUED - Egy CALL REQUEST csomag beérkezett, de még nem történt LISTEN hívás. 4. ESTABLISHED - Az összeköttetés létrejött. 5. SENDING - A felhasználó engedélyre vár a csomag elküldéséhez. 6. RECEIVING - Egy RECEIVE hívás történt. 7. DISCONNECTING (DISCONN) - Egy helyi
DISCONNECT hívás
történt.
Állapotátmenet a következő események hatására történik: primitív végrehajtása, csomag érkezése vagy időtúllépés. A 6.20. ábrán látható eljárások két típusba sorolhatók. Legtöbbjüket közvetlenül hívja a felhasználói program, azonban a packet_arrival és clock rutinok eltérően mű ködnek. Spontán aktivizálódnak külső események hatására: csomag érkezésére, illetve óraütemre. Ezek valójában megszakítás-kezelő rutinok. Feltételezzük, hogy sohasem kerülnek meghívásra szállítási entitás eljárás végrehajtása közben, csak akkor kerülhet rájuk a vezérlés, amikor a felhasználói program alszik, vagy a szállítási entitáson kí vüli része fut. Ez a tulajdonság kulcsfontosságú a kód helyes működéséhez.
562
SZÁMÍTÓGÉP-HÁLÓZATOK
#define MAX_CONN 32 #define MAX_MSG_SIZE 8192 #defíneMAX_PKT_SIZE 512 #define TIMEOUT 20 #define CRED 1 #define OK 0 #define #define #define #define
/* egyidejű összeköttetések maximális száma 7 /* üzenet maximális hossza bájtokban 7 /* csomag maximális hossza bájtokban */
ERR_FULL -1 ERR_REJECT -2 ERR_CLOSED -3 LOW_ERR -3
typedef int transport_address; typedefenum{CALL_REQ,CALL_ACC,CLEAR_REQ,CLEAR_CONF,DATA_PKT,CREDIT}pkt^type; typedef enum{IDLE,WAITING,QUEUED,ESTABLISHED,SENDING,RECEIVING,DISCONN}cstate; /* globális változók 7 transport_address listen_address; /* a hallgatni kívánt helyi cím 7 int listen_conn; /* összeköttetés-azonosító hallgatásra 7 unsigned char data[MAX_PKT_SIZE]; /* átmeneti terület adatcsomagok részére 7 struct conn { transporLaddress local_address, remote_address; cstate state; /* az összeköttetés állapota 7 unsigned char *user_buf_addr; /* mutató a vételi pufferre 7 int byte_count; /* adási/vételi mennyiség 7 int clr_req__received; /* CLEAR_REQ vételekor beállítva 7 int timer; /* CALL_REQ csomagok időtúllépés számlálója 7 int credits; /* elküldhető csomagok száma 7 }conn[MAX_CONN + 1]; /* slot 0 nem használjuk*/ void sleep(void); /* prototípusok 7 void wakeup(void); void to_net(int cid, int q, int m, pktjype pt, unsigned char *p, int bytes); void from_net(int *cid, int *q, int *m, pktjype Tpt, unsigned char *p, int *bytes); int listen(transport_address t) {/* A felhasználó egy összeköttetésre vár. Ellenőrizzük, hogy érkezett-e CALL_REQ csomag. 7 int i = 1, found = 0; for (i = 1; i <= MAX_CONN; i++) /* A táblázatban CALL_REQ -t keresünk. 7 if (conn[i].state == QUEUED && conn[i].locaLaddress == t) { found = i; break; } if (found == 0) { 6.20. ábra. Példa szállítási entitásra
563
A SZÁLLÍTÁSI RÉTEG
/* Nincs várakozó CALL_REQ. Amíg nem érkezik egy, vagy le nem jár az időzítő, aludj.*/ Iisten_address = t; sleep(); i = listen_conn ; conn[i].state = ESTABLISHED; conn[i].timer = 0; listen_conn = 0; to_net(i, 0, 0, CALL_ACC, data, 0); return(i);
/* Az összeköttetés létrejött. 7 /* Időzítőt nem használjuk. */ /* Érvénytelen címet állítunk be. */ /* A hálózatot összeköttetés elfogadására szólítjuk fel. I* Visszatérési érték az összeköttetés azonosítója. 7
int connect(transport_address I, transport_address r) {/ *A felhasználó egy távoli folyamattal akar összeköttetést létesíteni; CALL__REQ csomagot küldünk. 7 int i; struct conn *cptr;
data[0] = r; data[1] = l; /* CALL_REQ csomag tartalma 7 i = MAX_CONN; /* Visszafelé keresünk a táblázatban. 7 while (conn[i].state != IDLE && i > 1) i = i - 1; if (conn[i].state == IDLE) { /* A CALL_REQ csomag elküldését jelző bejegyzést hozunk létre. 7 cptr = &conn[i]; cptr->local_address = I; cptr->remote_address = r; cptr->state = WAITING; cptr->clr_req_received = 0; cptr->credits = 0; cptr->timer = 0; to_net(i, 0, 0, CALL_REQ, data, 2); sleep(); /* CALL_ACC vagy CLEAR_REQ csomagra várunk. 7 if (cptr->state == ESTABLISHED) retum(i); if (cptr->clr_req_received) { /* A másik fél elutasította a kérést. 7 cptr->state = IDLE; /* Visszatérünk IDLE állapotba. 7 to_net(i, 0, 0, CLEAR_CONF, data, 0); retum(ERR_REJECT); } } else retum(ERR_FULL); /* Sikertelen CONNECT: nincs hely a táblázatban. 7 } int send(int cid, unsigned char bufptrQ, int bytes) { /* A felhasználó üzenetet akar küldeni. 7 int i, count, m; struct conn *cptr = &conn[cid]; I* SENDING állapotba lépünk. 7 cptr->state = SENDING; cptr->byte_count = 0; /* az üzenet már továbbított bájtjainak száma 7 if (cptr->clr_req_received == 0 && cptr->credits == 0) sleepQ; 6.20. ábra. Példa szállítási entitásra (folytatás)
564
SZÁMÍTÓGÉP-HÁLÓZATOK
if (cptr->clr_req_received == 0) { /* Van hitelünk, szükség esetén több csomagra vágjuk az üzenetet. */ do{ if (bytes - cptr->byte_count > MAX_PKT_SIZE) { /* többcsomagos üzenet */ count = MAX_PKT_SIZE; m = 1; /* további csomagok következnek */ } else { /* egycsomagos üzenet 7 count = bytes - cptr->byte__count; m = 0; /* utolsó csomag az üzenetből 7 } for (i = 0; i < count; i++) data[i] = bufptr[cptr->byte__count + i]; to_net(cid, 0, m, DATA_PKT, data, count); /* Egy csomagot elküldünk. 7 cptr->byte_count = cptr->byte_count + count; /* Növeljük a már továbbított bájtok száma 7 } while (cptr->byte_count < bytes); /* ciklus, amíg a teljes üzenet el nem ment 7 cptr->credits-; /* Minden üzenet egy hitelt használ fel. 7 cptr->state = ESTABLISHED; return(OK); } else { cptr->state = ESTABLISHED; retum(ERR_CLOSED); /* Sikertelen átvitel: a társentitás bontást kezdeményeze 7 } } int receive(int cid, unsigned char bufptr[], int *bytes) {/* A felhasználó fölkészül egy üzenet vételére. 7 struct conn *cptr = &conn[cid]; if (cptr->clr_req_received == 0) { /* Az összeköttetés él, próbálunk venni. 7 cptr->state = RECEIVING; cptr->user_buf_addr = bufptr; cptr->byte_count = 0; data[0] = CRED; data[1] = 1; to_net(cid, 1,0, CREDIT, data, 2); /* CREDIT csomagot küldünk. 7 sleepO; I* Blokkol, míg adat nem érkezik. 7 *bytes = cptr->byte_count; } cptr->state = ESTABLISHED; retum(cptr->clr_req^received ? ERR_CLOSED : OK); } int disconnect(int cid) { /* A felhasználó bontani akarja az összeköttetést. 7 struct conn *cptr = &conn[cid]; if (cptr->clr_req_received) {
/* A másik fél már bontást kezdeményezett. 7
6.20. ábra. Példa szállítási entitásra (folytatás)
A SZÁLLÍTÁSI RÉTEG
565
cptr->state = IDLE; /* Az összeköttetés véget ért. */ to_net(cid, 0, 0, CLEAR_CONF, data, 0); } else { /* Mi kezdeményezünk bontást. */ cptr->state = DISCONN; /* Nem történik bontás, míg a társentitás bele nem egyezik.*/ to_net(cid, 0, 0, CLEAR_REQ, data, 0); } retum(OK); } void packet_arrival(void) { /* Egy csomag megérkezett, fogjuk és feldolgozzuk. */ int cid; /* az összeköttetés, melyen a csomag befutott 7 int count, i, q, m; pktjype ptype; /*CALL_REQ, CALL^ACC, CLEAR_REQ, CLEAR_CONF, DATA_PKT, CREDIT*/ unsigned char data[MAX_PKT_SIZE]; /* a beérkező csomag adatrésze */ struct conn *cptr; from_net(&cid, &q, &m, Sptype, data, &count); /* elhozzuk */ cptr = &conn[cid]; switch (ptype) { case CALL_REQ: /* A távoli felhasználó összeköttetést akar létrehozni. cptr->local_address = data[0]; cptr->remote_address = data[1]; if (cptr->local„address == listen_address) { listen_conn = cid; cptr->state = ESTABLISHED; wakeup(); } else { cptr->state = QUEUED; cptr->timer = TIMEOUT; } cptr->clr_req_received = 0; cptr->credits = 0; break; case CALL_ACC:
/* A távoli felhasználó elfogadta a CALL_REQ csomagunkat. */
cptr->state = ESTABLISHED; wakeup(); break; case CLEAR_REQ:
/* A távoli felhasználó bontani akar, vagy visszautasítj hívásunkat. */
cptr->clr_req_received = 1; if (cptr->state == DISCONN) cptr->state = IDLE; /* elkerüljük az ütközést */ if (cptr->state == WAITING II cptr->state == RECEIVING II cptr->state == SENDING) wakeup(); break; case CLEAR_CONF:
/* A távoli felhasználó beleegyezik a bontásba. */
6.20. ábra. Példa szállítási entitásra (folytatás)
566
SZÁMÍTÓGÉP-HÁLÓZATOK
cptr->state = IDLE; break; case CREDIT: /* A távoli felhasználó adatra vár. */ cptr->credits += data[1 ]; if (cptr->state == SENDING) wakeup(); break; case DATA_PKT: /* A távoli felhasználó adatot küldött. 7 for (i = 0; i < count; i++) cptr->user__buf_addr[cptr->byte_count + i] = data[i]; cptr->byte_count += count; if (m == 0 ) wakeup(); } } void clock(void) {/* Ugrott egyet az óramutató, megvizsgáljuk a várakozó összeköttetés-kérések időzítéseit. 7 int i; struct conn *cptr; for (i = 1; i <= MAX__CONN; i++) { cptr = &conn[i]; if (cptr->timer > 0) { cptr->timer-; if (cptr->timer == 0) {
/* Jár az időzítő. 7
/* Most járt le az időzítő. 7 cptr->state = IDLE; to_net(i, 0, 0, CLEAR_REQ, data, 0);
} } } }
6.20. ábra. Példa szállítási entitásra (folytatás) A csomag fejrészében elhelyezkedő Q (Qualifier - megkülönböztető) bit lehetővé teszi a szállítási protokoll fejrész elhagyását, ami továbbítási overheaddel járna. A kö zönséges adatüzeneteket Q = 0 beállítással küldjük. A szállítási protokoll vezérlőüze netei - melyekből példánkban csak egy van (CREDIT) - Q = 1 értékkel ellátott adatüze netek. A fogadó szállítási entitás detektálja és feldolgozza ezeket az üzeneteket. A szállítási entitásban használt legfontosabb adatstruktúra a conn tömb, melyben minden lehetséges összeköttetés számára egy rekord van fönntartva. Ebben nyilván tartjuk az összeköttetés állapotát, beleértve mindkét végpont szállítási címét, az össze köttetésen elküldött és fogadott üzenetek számát, a jelenlegi állapotot [a fordító meg jegyzése: ez beleérthető az „összeköttetés állapota"-ba], a felhasználói puffer muta tóját, az aktuális üzenet már elküldött vagy vett bájtjainak számát, egy bitet, amely jel zi, hogy a távoli felhasználó DISCONNECT hívást adott ki, egy időzítőt, és egy hitel számlálót, ami az üzenetek küldését szabályozza. Egyszerű példánkban nem használ juk az összes itt felsorolt mezőt, de egy összetett szállítási entitás esetleg további me-
A SZÁLLÍTÁSI RÉTEG
567
zőket igényelne. Feltételezzük, hogy inicializáláskor minden conn bejegyzés IDLE ál lapothoz rendelődik. Amikor a felhasználó meghívja a CONNECT rutint, a szállítási entitás a hálózati réte get CALL REQUEST csomag küldésére utasítja, a felhasználó folyamatot pedig elaltatja. Amint a másik félhez megérkezik a CALL REQUEST csomag, a szállítási entitás meg szakítást kap, melyet a packet_arrival eljárás kezel le. Megvizsgálja, hogy figyel-e felhasználó a megadott címen. Ha igen, CALL ACCEPTED csomagot küld vissza, ami nek hatására a távoli felhasználó felébred, különben a CALL REQUEST IDŐTARTAM számú óraütésig várakozó sorban várakozik. Ha ezen az idó'n belül történik egy LISTEN hívás, akkor az összeköttetés létrejön; egyébként a várakozási sor időzítője le jár, és a szállítási entitás egy CLEAR_REQUEST csomaggal utasítja el a kérést. Ezzel lehet elkerülni, hogy a másik oldal örökké várakozzon. Jóllehet kiküszöböltük a szállítási protokoll fejrész használatát, mivel egyidejűleg több összeköttetés létezhet, szükségünk van egy olyan módszerre, melynek segítségé vel nyomon követhetjük, hogy mely csomag melyik szállítási összeköttetéshez tarto zik. A legegyszerűbb megközelítés szerint használjuk a hálózati réteg virtuális áram kör számát az összeköttetés azonosítójaként is. Ezenfelül a virtuális áramkör sorszáma indexként használható a conn tömbben. Amennyiben egy csomag a hálózati réteg k sorszámú virtuális áramkörén érkezik, a k-ik szállítási összeköttetéshez tartozik, mely nek állapotát az conn[k] rekord tartalmazza. Hosztban kezdeményezett összekötteté sek esetén az összeköttetés sorszámát a kezdeményező szállítási entitás választja. Be érkező hívások esetén a hálózati rétegé a választás joga. Tetszőleges, eddig nem hasz nált virtuális áramkör sorszámot választhat. Annak érdekében, hogy a szállítási entitáson belül ne kelljen puffereket biztosítani és kezelni, itt egy olyan forgalomszabályozási módszert használunk, amely eltér a szokásos csúszóablakos megoldástól. Amikor a felhasználó egy RECEIVE hívást hajt végre, egy különleges hitel üzenet (credit message) kerül elküldésre a küldő gép szállítási entitásának, és kerül a conn tömbbe feljegyzésre. SEND végrehajtásakor a küldő szállítási entitás megvizsgálja, hogy érkezett-e hitel az adott összeköttetésen. Ha igen, elküldi az üzenetet (szükség esetén több csomagban), és csökkenti a hitelszám lálót, különben újabb hitelüzenet érkezéséig alvó állapotba lép. Ez a mechanizmus ga rantálja, hogy a küldő fél nem továbbít üzenetet, amíg a fogadó nem hajtott végre RECEIVE eljárást. Ennek eredményeként mindig garantáltan lesz szabad puffer a beér kező üzenet számára. Ez a módszer egyszerűen általánosítható oly módon, hogy a ve vők többszörös puffereket kapjanak, és így többszörös üzeneteket is kérhessenek. Figyeljük meg a 6.20. ábrán látható program egyszerűségét. Egy valóságos szállítá si entitás minden felhasználó által biztosított paraméter érvényességét ellenőrizné, tá mogatná a hálózati réteg összeomlása utáni újraindulást, foglalkozna a hívásütközé sekkel, és egy jóval általánosabb szállítási szolgálatot biztosítana olyan lehetőségek kel, mint megszakítások, datagramok, valamint a RECEIVE és SEND primitívek nem blokkolódó változatai.
568 6.3.3.
SZÁMÍTÓGÉP-HÁLÓZATOK
A példa mint véges állapotú gép
Egy szállítási entitás megírása nehéz és munkaigényes feladat, különösen összetettebb szállítási protokollok használata esetén. A hibázás lehetőségének csökkentése érdeké ben gyakran hasznos lehet a protokoll állapotát véges állapotú gépként ábrázolni. Már láttuk, hogy mintaprotokollunk összeköttetésenként hét állapottal rendelkezik. Tizenkét esemény különböztethető meg, melyek hatására az összeköttetés egyik álla potból a másikba léphet. Ezek közül öt az öt szolgálati primitív meghívása, további hat a hat különböző legális csomagtípus, az utolsó pedig az időtúllépés. A 6.21. ábrán láthatjuk a protokoll főbb tevékenységeit mátrix formában. Az oszlopok az állapotok nak, a sorok a 12 eseménynek felelnek meg. A mátrix (azaz a véges automata) összes bejegyzése legfeljebb három mezővel ren delkezhet: egy predikátummal, egy tevékenységgel és az új állapottal. A predikátum azt jelzi, hogy milyen feltételek mellett fut le a tevékenység. Például a bal fölső mező ben, ha LiSTEN-t hajtunk végre és nincs több hely a táblázatban (Pl predikátum), a LISTEN hívás sikertelen lesz és nem történik állapotváltás. Másrészt, ha olyan címre érkezik CONNECT REQUEST, amelyen már hallgat egy felhasználó (P2 predikátum), az összeköttetés azonnal létrejön. Egy másik lehetőség az, ha P2 hamis, tehát nem érke zett CALL REQUEST csomag, így az összeköttetés IDLE állapotban marad továbbra is CALL REQUEST-re várakozva. Érdemes rámutatnunk, hogy a mátrixban használt állapotokat nem határozza meg teljes egészében maga a protokoll. Ebben a példában nincs LISTENING állapot, ami a LISTEN végrehajtásának logikus következménye lehetne. Erre azért nincs szükség, mert az állapot az összeköttetés bejegyzéséhez van hozzárendelve, és a LISTEN nem hoz létre új rekordot az összeköttetés számára. Miért nem? Mert úgy döntöttünk, hogy a hálózati réteg virtuális áramkör sorszámait használjuk az összeköttetések azonosítói ként. A LISTEN számára a virtuális áramkör számát a hálózati réteg határozza meg, amikor a CALL REQUEST csomag megérkezik. Az A1-A12 tevékenységek a főbb tevékenységek, úgy mint csomag küldése vagy időzítő elindítása. Nem soroltuk fel az összes melléktevékenységet (például egy össze köttetés rekordjának inicializálása). Ha egy tevékenység végrehajtása során egy alvó folyamatot is fölébresztünk, az ébredés utáni tevékenységek szintén számítanak. Pél dául, ha egy CALL REQUEST csomag érkezik, melyre egy alvó folyamat várakozik, a CALL REQUEST csomag által kiváltott tevékenységbe beletartozik a CALL ACCEPT cso mag elküldése közvetlenül ébredés után. Miután az összes tevékenység lefutott, az összeköttetés új állapotba léphet, ahogy azt a 6.21. ábrán láthatjuk. A protokoll mátrix formában történő ábrázolása háromszoros előnnyel jár. Egy részt ebben az alakban jóval könnyebb a programozónak szisztematikusan ellenőrizni az események és állapotok összes kombinációjában, hogy szükséges-e valamilyen te vékenységet elvégezni. Valóságos alkalmazásoknál egyes kombinációk hibakezelésre használhatók. A 6.21. ábrán nem különböztettük meg a lehetetlen és az érvénytelen szi tuációkat. Például, ha egy összeköttetés várakozik állapotban van, a DISCONNECT ese mény lehetetlen, mert a felhasználó blokkolva van, és egyáltalán nem hajthat végre pri mitíveket. Másrészt küld állapotban a szállítási entitás nem vár adatcsomagot, mert nem adott hitelt a távoli entitásnak. Adatcsomag érkezése ilyenkor protokollhibát jelent.
569
A SZÁLLÍTÁSI RÉTEG
Állapot Idle
LISTEN
P1: -/Idle P2: A1/Estab P2: A2/ldle
CONNECT
P1:-/ldle P1:A3/Wait
Waiting
DisQueued Established Sending Receiving connecting -/Estab
CB
E
DISCONNECT
P4: A5/ldle P4: A6/DÍSC
SEND
P5: A7/Estab P5: A8/Send
RECEIVE
A9/Receiving
CL
CalLreq
P3: AVEstab P3: A4/Queu'd
Call_acc
- /Estab
E o
Clear_req
- /Idle
°N <
Clear_conf
o CB
co
0)
A10/Estab
A10/Estab
A10/Estab
-/Idle
-/Idle
-cu CD
m
DataPkt
A12/Estab
Credit sg
<( Időtúllépés
Predikátumok P1: Betelt az összeköttetések táblázata P2: Call_req függőben P3: LISTEN függőben P4: Clear_req függőben P5: Van felhasználható hitel
A11/Estab
A7/Estab
-/Idle
Tevékenységek A1: Call_acc küldése A2: Várakozás Call_req-ra A3: Call_req küldése A4: Időzítő indítása A5: Clear_conf küldése A6: Clear_req küldése A7: Üzenet küldése
A8: Várakozás Creditre A9: Credit küldése A10: Clr_Req-Vétel 1-re állítása A11: Credit feljegyzése A12: Üzenet elfogadása 6.21. ábra. A mintaprotokoll, mint véges állapotú gép. Minden bejegyzés egy opcionális predikátumot, egy opcionális tevékenységet és egy új állapotot tartalmaz. A hullám (~j azt jelzi, hogy nincs jelentősebb esemény. Felülvonás a predikátum felett annak ne gáltját jelenti. Az üres mezők érvénytelen vagy lehetetlen eseményt jelölnek
570
SZÁMÍTÓGÉP-HÁLÓZATOK
CONNECT
r~ •
/
^
mi n L
CLEAR REQ
•
~ O
H
. LU
z z
LU
z tr
WAITING <~AI i W , 1 U L
-
•
o
QUEUED
ü
Q ' ESTABLISHED s~ . f CRr=DIT, CLEAR REQ
( V
Ar.n r-\\-'W
/
3NNECT
SEND
O
j
LISTEN
m
SENDING
A
CALLREQ
RECEIVE
x 1
V
Q
DATA, CLEAR REQ
RECEIVING
CLEAR REQ CLEAR r.DMF
DISCONNECTING
6.22. ábra. A mintaprotokoll grafikus formában. Az összeköttetés állapotát nem módosító állapotátmeneteket elhagytuk az áttekinthetőség kedvéért A protokoll mátrix alakú ábrázolásának második előnye megvalósításkor jelentke zik. Képzeljünk el egy kétdimenziós tömböt, amelynek minden a[i]\j] eleme egy mu tató vagy index egy eljárásra, ami az i esemény kezelésére szolgál, ha az összeköttetés aj állapotban van. Egy lehetséges megvalósítás az, amikor a szállítási entitást egy rö vid ciklus formájában írjuk meg, melynek elején az entitás egy esemény bekövet kezésére várakozik. Amikor az esemény megtörtént, az entitás megkeresi a megfelelő összeköttetést, és megállapítja az állapotát. Az esemény és az állapot ismeretében a szállítási entitás egyszerűen meghívja az a tömb megfelelő eljárását. Ez a megközelí tés a mi példánknál jóval szabályosabb, szisztematikusabb tervezést tesz lehetővé. A véges automata megközelítés harmadik előnye a protokollok leírásánál jelentke zik. Némely szabvány dokumentumában a protokollok a 6.21. ábráéhoz hasonló véges állapotú géppel vannak megadva. Az ilyen jellegű leírástól a működő szállítási entitá sig jóval egyszerűbb út vezet, ha a szállítási entitást a szabványban rögzítetteken ala puló véges állapotú gép vezérli. A véges automata megközelítés legfőbb hátránya az, hogy esetleg jóval nehezeb ben érthető, mint a kezdetben használt közvetlenül kódolt példánk. Ezt a problémát azonban részben megoldhatjuk úgy, hogy a véges állapotú gépet gráfként is ábrázol juk, mint azt a 6.22. ábrán is láthatjuk.
6.4.
Az Internet szállítási protokolljai: az UDP
Az Internet két fő protokollt használ a szállítási rétegben, az egyik összeköttetés ala pú, a másik összeköttetés nélküli. A következő szakaszokban mindkettőt tanulmá nyozni fogjuk. Az összeköttetés nélküli protokoll az UDP, az összeköttetés alapú pe-
571
A SZÁLLÍTÁSI RÉTEG
dig a TCP. Az UDP-vei fogjuk kezdeni a tárgyalást, mivel ez lényegében csak egy rö vid fejrészt tesz az IP-csomagokba. Az UDP két gyakorlati alkalmazását is meg fog juk vizsgálni.
6.4.1.
Bevezetés az UDP-be
Az Internet protokollkészlete egy összeköttetés nélküli protokollt is támogat, ez az UDP (User Datagram Protocol - felhasználói datagram protokoll). Az UDP olyan alkalmazásoknak kínálja a szolgálatát, amelyek összeköttetés kiépítése nélkül akarnak beágyazott IP-datagramokat küldeni. Az UDP leírása a 768-as RFC-ben található. Az UDP olyan szegmenseket (segment) használ az átvitelhez, amelyek egy 8 bájtos fejrészből, valamint a felhasználói adatokból állnak. A fejrész a 6.23. ábrán látha tó. A két port a végpontok forrás- és a célgépen belüli azonosítására szolgál. Amikor egy UDP-szegmens megérkezik, akkor az adatmezejét a szállítási entitás kézbesíti a címzett portra kapcsolódó folyamatnak. A folyamatok a BIND vagy más hasonló pri mitív használatával kapcsolódhatnak rá egy portra, hasonlóan a TCP 6.6. ábrán is lá tott esetéhez (a kötési folyamat az UDP-nél is ugyanaz). Az UDP használatának tulaj donképpen az a legnagyobb előnye a nyers IP használatával szemben, hogy a fejrész ben megtalálható a feladó és a címzett portszáma is. A portszámokat tartalmazó me zők nélkül a szállítási réteg nem tudná, hogy mit kezdjen a csomaggal. A segítségük kel azonban helyesen tudja kézbesíteni a szegmenseket. A forrás portjára elsősorban akkor van szükség, amikor választ kel küldeni a fel adónak. A választ küldő folyamat úgy tudja megadni, hogy az üzenete a célgép me lyik folyamatának szól, hogy a bejövő szegmens forrásport mezőjét átmásolja a ki menő szegmens célport mezőjébe. Az UDP-szegmens hossza mező a 8 bájtos fejrész és az adatmező együttes hosszát tartalmazza. Az UDP ellenőrző összeg mező használata nem kötelező és 0-t tartalmaz abban az esetben, ha nem számították ki (egy 0-nak kiszámolt összeget csupa l-esként adnak meg). Kikapcsolni azonban nem okos dolog, kivéve, ha egyáltalán nem fontos az átvitt adatok minősége (pl. digitalizált beszéd). Valószínűleg érdemes néhány olyan feladatot külön is megemlíteni, amit az UDP nem végez el. Az UDP nem végez forgalomszabályozást, hibakezelést vagy újraküldést egy rossz szegmens vétele esetén. Ez mind a felhasználói folyamatokon múlik. Az amit elvégez, az az, hogy biztosít egy interfészt az IP-protokoll használatához, az zal a többletszolgáltatással, hogy a portok használatával egyszerre több folyamatot képes demultiplexelni. Ez minden, amit az UDP kínál. Az olyan alkalmazásoknak, 32 bit
I
I
I
I
I
I
I
I
I
I
I
I
I
I
I
I
I
•• L—l L _ l
I
I
I
1
I
I
I
I
Forrásport
Célport
UDP szegmens hossza
UDP ellenőrző összeg
6.23. ábra. Az UDP-fejrész
I
I
I
I
572
SZÁMÍTÓGÉP-HÁLÓZATOK
amelyeknek fontos a csomagforgalom, a hibakezelés vagy az időzítés precíz ellenőr zése, az UDP pontosan azt nyújtja, amire szükségük van. A kliens-szerver-alkalmazásoké például olyan terület, amelyen az UDP kifejezet ten hasznos. A kliens gyakran küld olyan rövid kéréseket a szervernek, amelyekre rö vid választ vár. Ha vagy a kérés vagy a válasz elvész, a kliens egyszerűen megvárja, amíg az időzítő lejár, és újra próbálkozik. így nem csak a kód egyszerűbb, de keve sebb üzenetre is van szükség (egy-egy üzenet mindkét irányban), mint egy összeköt tetés felépítését igénylő protokollban. A DNS (Domain Name System - körzeti névkezelő rendszer) például egy olyan alkalmazás, amely ilyen módon használja az UDP-t. A DNS-ről részletesen a 7. feje zetben lesz szó. Röviden erről most csak annyit, hogy minden olyan programnak, amely valamely hoszt neve (pl. www.cs.berkeley.edu) alapján akarja kikeresni a hoszt IP-címét, egy UDP-csomagot kell elküldenie valamelyik DNS-szervernek. A szerver egy olyan UDP-csomaggal válaszol, amely a hoszt IP-címét tartalmazza. Nincs szük ség előzetes összeköttetés-létesítésre, sem az összeköttetés lebontására az átvitel után. A hálózaton csak két üzenet halad át.
6.4.2.
Távoli eljáráshívás
Amikor egy hoszt üzenetet küld egy másik, távoli hosztnak és választ is kap arra, az egy bizonyos értelemben nagyon hasonlít a programozási nyelvek eljáráshívására. A kommunikációt mindkét esetben egy vagy több paraméterrel indítjuk, és egy ered ményt kapunk vissza. Ez a megfigyelés arra késztette a mérnököket, hogy eljáráshívá sok formájában próbálják meg lebonyolítani a hálózatokon folyó kérés-válasz jellegű kommunikációt. Egy ilyen elrendezés sokkal könnyebbé teszi a hálózati alkalmazások programozását és a használatukat is egységesebbé teszi. El lehet képzelni például egy olyan, get_IP_address(hoszt_név) nevű eljárást, amely úgy működik, hogy egy UDPszegmenst küld valamelyik DNS-szervernek és megvárja a választ, szükség esetén egy bizonyos idő után újrapróbálkozva, ha egyetlen próbálkozás nem lenne elegendő. Ez zel a hálózat működésének minden részletét el lehet rejteni a programozók elől. A munka kulcsfontosságú részét ezen a területen Birrell és Nelson (1984) végezte. Dióhéjban összefoglalva, Birrell és Nelson azt javasolta, hogy a programok hívhassa nak távoli hosztokon futó eljárásokat is. Amikor az 1. gép egyik folyamata meghív egy eljárást a 2. hoszton, az 1. hoszt rendszere a hívó folyamatot felfüggeszti és a 2. hoszton megkezdődik a hívás végrehajtása. A szükséges információt a hívótól a hívott felé a paraméterekben, visszafelé pedig az eljárás eredményében lehet átvinni, a prog ramozó elől azonban minden üzenetváltás rejtve marad. Ezt a módszert RPC-nek (Remote Procedure Call - távoli eljáráshívás) nevezték el és sok hálózati alkalma zás alapjául szolgált már. A hívó folyamatot hagyományosan kliensnek, a hívott fo lyamatot pedig szervernek hívják. Mi is ezeket a neveket fogjuk használni. Az RPC alapötlete az, hogy a távoli eljáráshívásoknak minél jobban hasonlítaniuk kell a helyiekhez. A legegyszerűbb formájában a kliensprogramnak a távoli eljárás meghívásához egy kis könyvtári függvényhez kell kapcsolódniuk, amelyet kliens csonknak (client stub) neveztek el. Ez a függvény képviseli a szervereljárást a kliens
573
A SZÁLLÍTÁSI RÉTEG
címterében. Ehhez hasonlóan, a szerver egy szervercsonknak (server stub) nevezett eljárással áll kapcsolatban. Ezek az eljárások azt a tényt rejtik el, hogy a kliens függ vényhívása nem helyben fut le, hanem a szerveren. Egy távoli eljáráshívás gyakorlati lépéseit a 6.24. ábra szemlélteti. Az elsó' lépés ben a kliens meghívja a klienscsonkot. Ez a hívás egy helyi eljáráshívás, így a para méterei a megszokott módon a verembe kerülnek. A második lépésben a klienscsonk belepakolja a paramétereket egy üzenetbe és egy rendszerhívást hajt végre, amellyel elküldi ezt a csomagot. A paraméterek csomagba pakolását rendezésnek (marshal ing) nevezik. A harmadik lépésben a kernel átküldi az üzenetet a kliensgépről a szer vergépre. A negyedik lépés az, hogy a szerver kernele átadja a bejövó' csomagot a szervercsonknak. Végül az ötödik lépésben a szervercsonk meghívja a szervereljárást a visszarendezett paraméterekkel. A válasz ugyanezen az útvonalon halad végig az ellenkező irányban. A dolog legfontosabb része az, hogy a felhasználó által írt kliensfolyamat egy olyan, teljesen szokványos (vagyis helyi) eljáráshívást hajt végre a klienscsonkon, amelynek a neve is ugyanaz, mint a szervereljárásnak. Mivel a klienseljárás és a kli enscsonk ugyanabban a címtérben van, a paramétereket a szokásos módon lehet átad ni. Ehhez hasonlóan a szervereljárást is egy olyan folyamat hívja meg, amely a saját címterében van, olyan paraméterekkel, amilyeneket vár. A szervereljárás nem érzékel semmi szokatlan dolgot. Ezen a módon tulajdonképpen a hálózati kommunikációt szokványos eljáráshívásnak álcázzuk ahelyett, hogy a csatolókon keresztül intéznénk a be- és kivitelt. Az RPC-nek minden elméleti eleganciája ellenére van néhány rejtett hátulütője is. Ezek közül az egyik a mutató típusú paraméterek használatakor jelentkezik. Általában nem baj, ha egy eljárásnak mutatót kell átadni, mert a meghívott eljárás a hívóval tel jesen azonos módon használhatja azt, mivel a két eljárás ugyanabban a virtuális cím zési térben létezik. RPC-vel lehetetlen mutatókat átadni, mert a kliens és a szerver két különböző címtérben helyezkedik el. Egyes esetekben azért bizonyos trükkök bevetésével megvalósítható a mutatók át adása. Tegyük fel, hogy az első paraméter egy olyan mutató, amely egy k egész szám ra mutat. A klienscsonk a tényleges k számot rendezi be a csomagba, és azt küldi el a Kliens CPU
(
Kliens
\
•*-->.
Szerver CPU Szervercsonk'V^
^KliensNcsonk
V Szerver/
Ü Operációs rendszer' '
<
..Operációs rendszer 3
J
6.24. ábra. Egy távoli eljáráshívás végrehajtásának lépései. A csonkokat satírozással jelöltük.
574
SZÁMÍTÓGÉP-HÁLÓZATOK
szervernek. A szervercsonk létrehoz egy mutatót k-ra. és ezt adja át a szervereljárásnak pontosan úgy, ahogyan az eljárás azt elvárja. Amikor a szervereljárás visszaadja a ve zérlést a szervercsonknak, a csonk visszaküldi a k értéket a kliensnek, amely az új k-t bemásolja az eredeti helyére, mivel a szerver a feldolgozás során megváltoztathatta az értékét. A szokásos hivatkozásos eljáráshívást így gyakorlatilag másolással és vissza állítással helyettesítjük. Ez a trükk azonban sajnos nem mindig működik. Például nem alkalmazható, ha a mutató egy gráfra vagy más összetett adatstruktúrára mutat. Emiatt néhány korlátozást kell bevezetnünk a távolról meghívott eljárások paraméterezésében. A második gond az, hogy a gyengén típusos nyelvekben (pl. a C) teljesen szabá lyos olyan eljárást írni, amely anélkül számítja ki két vektor (tömb) belső szorzatát, hogy bármelyikük komponenseinek számát megadtuk volna. Az egyes vektorok adott esetben csak a hívó és a hívott eljárás által ismert értékekkel is le lehetnek zárva. Ilyen körülmények között a klienscsonknak lényegében lehetetlen a paraméterek csoma gokba rendezése; mivel sehogyan sem tudja kideríteni a méretüket. A harmadik probléma az, hogy nem mindig lehetséges kikövetkeztetni a paraméte rek típusát, még egy formális specifikációból vagy magából a kódból sem. Erre egy jó példa a printf, amely tetszőleges számú paraméterrel (de legalább eggyel) rendelkez het, és a paraméterei egészek, rövid és hosszú ábrázolású számok, karakterek, füzérek, különféle hosszúságú lebegőpontos számok és más típusok tetszőleges keverékei le hetnek. A printf eljárás távoli hívása a gyakorlatban éppen azért lenne lehetetlen, mert a C ennyire engedékeny. Mindezek ellenére egy olyan szabály nem lenne népszerű, amely azt mondaná ki, hogy csak akkor használhatunk RPC-t, ha nem C-ben (vagy C++-ban) írjuk a programunkat. A negyedik probléma a globális változók használatával kapcsolatos. Rendes kö rülmények között a hívó és a hívott eljárás a paramétereken keresztül történő kommu nikáció mellett kommunikálhat globális változók használatával is. Ha a hívott eljárást most képzeletben áttesszük egy távoli gépre, akkor a kód nem fog működni, mivel a két eljárás már nem osztozik a globális változókon. Az RPC hiányosságainak fenti felsorolásával azonban nem azt akartuk kifejezni, hogy az RPC használhatatlan. Az RPC valójában széles körben használatos, de ahhoz azért szükség van néhány korlátozásra, hogy a gyakorlatban is jól működjön. Az RPC-nek természetesen nem kötelező UDP-szegmenseket használnia, de az RPC és az UDP jól illeszkednek egymáshoz, ezért az RPC megvalósításai gyakran használják az UDP-t. Ha azonban a paraméterek vagy az eredmények hosszabbak is lehetnek egy UDP-szegmens maximális méreténél, vagy ha az igényelt művelet nem idempotens (vagyis nem lehet biztonságosan megismételni, pl. egy számláló lépteté se), akkor a kérés elküldéséhez egy TCP-összeköttetés kiépítésére lehet szükség az UDP használata helyett.
6.4.3.
Valós idejű szállítási protokoll
A kliens-szerver RPC egy olyan terület, amelyen az UDP széles körben használatos. Egy másik ilyen a multimédiás alkalmazásoké. Ahogyan az internetes rádió, az internetes telefon, a hálózati zeneszolgáltatás (music-on-demand), a videokonferencia
575
A SZÁLLÍTÁSI RÉTEG
és más multimédiás alkalmazások egyre elterjedtebbé váltak, a tervezők észrevették, hogy minden egyes alkalmazáshoz többé-kevésbé ugyanazt a valós idejű szállítási protokollt találták fel újra és újra. Fokozatosan egyre nyilvánvalóbbá vált, hogy jó öt let lenne kitalálni egy általános, több célra alkalmazható valós idejű szállítási proto kollt, így született meg napjainkban a széles körben használatos RTP (Real-Time Transport Protocol - valós idejű szállítási protokoll), amelyet az 1889-es RFC ír le. Az RTP némileg furcsa helyet foglal el a többrétegű protokollkészletben. Az RTP-t a felhasználó címterébe utalták, és általában az UDP-re ráépülve fut. A működése a következő. A multimédiás alkalmazások több hang-, mozgókép-, szöveg- és esetleg egyéb folyamból épülnek fel. Ezeket betöltik az RTP-könyvtárba (RTP library), amely az alkalmazással együtt a felhasználói címtérben található. Ez a könyvtár multiplexeli és RTP-blokkokba kódolja a folyamokat, amelyeket ezután egy csatlako zóra (socket) továbbít. A csatlakozó másik végén (az operációs rendszer kernelében) ezekből UDP-szegmensek lesznek, amelyeket a rendszer IP-csomagokba ágyaz be. Amennyiben a számítógép egy Ethernetre csatlakozik, ezután az IP-csomagok Ethernet-keretekbe kerülnek az átvitelhez. A protokollok egymásra épülését ebben a helyzetben a 6.25.(a) ábra mutatja be. A 6.25.(b) ábrán az adategységek egymásba ágyazódása látható. Ennek a felépítésnek az egyik következményeként kissé nehéz meghatározni, hogy az RTP melyik rétegben van. Mivel a felhasználói területen fut és egy alkalmazási programmal áll kapcsolatban, határozottan úgy néz ki, mint egy alkalmazási protokoll. Másrészt viszont ez egy olyan általános, alkalmazás-független protokoll, amely csak szállítási lehetőséget nyújt, így nagyon hasonlít egy szállítási protokollra is. A legjobb leírás talán az, hogy az RTP olyan szállítási protokoll, amelyet az alkalmazási réteg ben valósítottak meg. Az RTP alapvető feladata az, hogy több valós idejű adatfolyamot multiplexeijen UDP-szegmensek egyetlen folyamába. Az UDP-folyamot egy címre (egyesküldés), vagy több címre (többesküldés) is feladhatja. Mivel az RTP csak a szabványos UDP-t használja, a blokkjait a routerek nem kezelik különleges módon, azt az esetet kivéve, Ethernet-fej irész IF'-fejrész
Felhasználói, terület
Multimédiás ajkaln-iazás RTF 7 """
UDP-fejrész RTP-fejrész
RTP-adatmező
Csatoló interfész OSÍ Kernel •
UDP IP Ethernet
-
UDP-adatmező IP-adatmező
• •
Ethernet-adatmező
(b) 6.25. ábra. (a) Az RTP helyzete az egymásra épülő protokollok között, (b) Az adategységek egymásba ágyazódása
576
SZÁMÍTÓGÉP-HÁLÓZATOK
ha a hálózaton valamelyik szokványos IP szolgáltatásminőségi tulajdonság engedé lyezve van. Ez elsősorban azt jelenti, hogy nincsenek különleges garanciák a kézbesí tésre, a sebességingadozásra stb. Egy RTP-folyamban minden blokk kap egy sorszámot, amely eggyel nagyobb az azt megelőző csomagénál. A címzett a sorszámozás segítségével tudja eldönteni, hogy hiányoznak-e blokkok. Ha egy blokk nem érkezik meg, a címzett legjobb lehetősége az, hogy interpolációval közelítse a hiányzó értéket. Az újraküldés ebben az esetben nem jó megoldás, hiszen az újra elküldött blokk valószínűleg túl későn érkezne meg ahhoz, hogy még használható legyen. Ennek következtében az RTP nem használ for galomszabályozást, hibakezelést, nyugtázást és újraküldést kérő megoldásokat. Minden RTP-adatmezőben több minta kaphat helyet, bármely olyan kódolásban, amelyet az alkalmazás használni akar. A közreműködés megkönnyítésére az RTP számos profilt definiál (pl. egyetlen hangfolyam) és minden profilhoz több kódolási formátumot enged meg. Például egy egyedüli hangfolyamot kódolhatunk 8 bites PCM mintákba 8 kHz-en, delta kódolással, prediktív kódolással, GSM-kódolással, MP3 formátumban és így tovább. Az RTP a fejrészben biztosít egy mezőt a forrásnak arra, hogy megjelölje a kódolást, de egyébként nem avatkozik bele a kódolás részleteibe. Az időbélyegek (timestamp) kezelése egy másik olyan lehetőség, amelyre sok va lós idejű alkalmazásban szükség van. Az alapötlete az, hogy a forrásgép minden blokk első mintájához egy időbélyeget rendel hozzá. Az időbélyegeket a folyam kezdetéhez kell viszonyítani, így csak az időbélyegek különbsége lényeges, az abszolút értékük nek nincs jelentése. Ez a megoldás lehetővé teszi a célgép számára, hogy egy kis puf fert használjon, és minden mintát megfelelő számú milliszekundummal a folyam kez dete után játsszon le, a mintát tartalmazó blokk megérkezési idejétől teljesen függetle nül. Az időbélyegek használata nem csak a sebességingadozások hatásait egyenlíti ki, de azt is lehetővé teszi, hogy több folyamot szinkronizáljunk egymással. Például, egy digitális televízióadás állhat egy mozgóképfolyamból és két hangfolyamból. A két hangfolyamot egyrészt sztereóadások sugárzására lehet használni, másrészt olyan fil mek sugárzására, amelyeknek mind az eredeti nyelvű hangsávját, mind a helyi nyelvre lefordított hangsávját egyszerre sugározzák, szabad választást kínálva a nézőnek. Minden folyam különböző fizikai eszközről érkezik, de ha ugyanazzal a számlálóval időbélyegezik, akkor szinkronban is le lehet azokat játszani. Ez akkor is megtehető, ha a folyamok el-eltévednek útközben. Az RTP fejrészét a 2.26. ábra szemlélteti. Három 32 bites szóból és esetleg néhány kiterjesztésből áll. Az első szó a kétbites Verzió mezőt tartalmazza, amely már 2-nél tart. Reméljük, hogy ez a verzió már nagyon közel van a végső verzióhoz, mivel már csak egy érték maradt kihasználatlan (bár a 3-at lehetne úgy definiálni, hogy egy ki terjesztő szóban leírt verziószámra utaljon). A P bit azt jelzi, hogy a blokkot 4 bájttal vagy annak többszörösével kipárnázhat ják (padding). Az utolsó pámázó bájt (padding byte) adja meg, hogy hány pámázó bájt került a csomagba. Az X bit azt jelzi, hogy a blokk egy kiterjesztő fejrésszel (eXtension header) rendelkezik. A kiterjesztő fejrész formátuma és értelme itt nincs meghatározva. Az egyetlen definiált dolog az, hogy a kiterjesztés első bájtja adja meg a kiterjesztés hosszát. Ez egy beépített vészmegoldás az előre nem látott követelmé nyek teljesítéséhez.
577
A SZÁLLÍTÁSI RÉTEG
-32 bit
Verzió
CC
Adatmező típusa
M
Sorszám
Időbélyeg Szinkronizációs forrás azonosítója
|
Közreműködő forrás azonosítója
6.26. ábra. Az RTP-fejrész A CC mező azt adja meg, hogy hány hozzájáruló forrás (lásd alább) van jelen, az értéke 0-tól 15-ig terjedhet. Az M bit egy alkalmazásfüggő jelölő bit (marker bit). Ez a bit jelölheti egy mozgókép-keret elejét, egy szó kezdetét egy hangcsatornán vagy bármi mást, amit az alkalmazás megért. Az Adatmező típusa mező adja meg a hasz nált kódolási algoritmust (tömörítetlen 8 bites hang, MP3 stb.). Mivel ez a mező min den csomagban jelen van, a kódolást akár adás közben is meg lehet változtatni. A Sor szám mindössze egy blokkszámláló, amely minden elküldött RTP-blokknál eggyel nő. Az elveszett blokkok felderítésére szolgál. Az időbélyeget a folyam forrása állítja elő, hogy feljegyezze az első blokk keletke zésének időpontját. Ez az érték segítséget nyújthat a dzsitter csökkentésében a vevő oldalán, mivel lehetővé teszi a lejátszás függetlenítését a blokkok érkezési idejétől. A Szinkronizációs forrás azonosítója azt adja meg, hogy a blokk melyik folyamhoz tar tozik. Ez a párhuzamos adatfolyamok egyetlen UDP-szegmensfolyamba való multi plexelését, majd demultiplexelését teszi lehetővé. Végül, a blokk tartalmazhat Hozzá járuló forrásazonosítója mezőket. Ezeket akkor használják, ha keverők is jelen van nak a forrásoldalon. Ebben az esetben a keverő a szinkronizációs forrás és minden egybekevert folyamot ezekben a mezőkben sorolnak fel. Az RTP-nek van egy kistestvére is, amelyet RTCP-nek (Real-Time Transport Control Protocol - valós idejű szállítási vezérlési protokoll) neveztek el. Ez a pro tokoll a visszacsatolást, a szinkronizációt és a felhasználói interfészt kezeli, de adato kat nem szállít. Az első tulajdonságát arra használhatjuk, hogy visszacsatolást bizto sítsunk a forrásgépeknek a késleltetésről, a sebességingadozásról, a sávszélességről, a torlódásokról és más hálózati tulajdonságokról. Ezt az információt a kódoló folyamat arra használhatja, hogy növelje az adatsebességet (és így javítsa a minőséget), amikor a hálózat jól működik, és visszavegye az adatsebességet, amikor baj van a hálózattal. A folyamatos visszacsatolás segítségével a kódoló algoritmusok mindig a lehető leg jobb minőséget tudják biztosítani, folyamatosan alkalmazkodva a pillanatnyi körül ményekhez. Például, ha a sávszélesség csökken vagy növekszik az átvitel során, akkor a kódolást szükség szerint váltogathatjuk az MP3 és a 8 bites delta kódolás között. Az
578
SZÁMÍTÓGÉP-HÁLÓZATOK
Adatmező típusa mező a célgépet az egyes blokkokhoz használt kódolási eljárásról tájékoztatja, így szükség esetén lehetővé teszi annak azonnali megváltoztatását. Az RTCP a folyamok közötti szinkronizációt is kezeli. A baj az, hogy a különböző folyamok más-más órákat használhatnak, amelyeknek a finomsága (granularity) és az elcsúszása (drift) is különböző lehet. Az RTCP használatával ezeket szinkronban lehet tartani. Végül, az RTCP arra is lehetőséget nyújt, hogy a különböző forrásokat (pl. ASCII szövegben) megnevezzük. Ezt az információt meg lehet jeleníteni a vevő képernyőjén, jelezve neki, hogy ki beszél éppen. Az RTP-ről további információ (Perkins, 2000) könyvében található.
6.5.
Az Internet szállítási protokollja: a TCP
Az UDP egy egyszerű protokoll és van néhány sajátos alkalmazása, mint például a kliens-szerver-interakciók és a multimédia. A legtöbb internetes alkalmazás azonban megbízható, sorrendhelyes kézbesítést igényel. Az UDP ezt nem tudja nyújtani, ezért egy másik protokollra is szükség van. Ezt a protokollt TCP-nek hívják és ez az Internet legfontosabb igáslova. Lássunk neki a részletes tanulmányozásának!
6.5.1.
A TCP bemutatása
A TCP-t (Transmission Control Protocol - átvitel-vezérlési protokoll) kifejezetten arra tervezték, hogy megbízható bájtfolyamot biztosítson a végpontok között egy egyébként megbízhatatlan összekapcsolt hálózaton. Egy összekapcsolt hálózat abban különbözik egyetlen hálózattól, hogy az egyes részeinek topológiája, sávszélessége, késleltetése, csomagmérete és más paraméterei nagymértékben különbözhetnek. A TCP-t arra tervezték, hogy dinamikusan alkalmazkodjon az összekapcsolt hálózatok tulajdonságaihoz, valamint hogy nagymértékben ellenálló legyen sokféle meghibáso dással szemben. A TCP-t formálisan a 793-as RFC definiálta, az idő előrehaladtával azonban sok hibára és belső ellentmondásra derült fény, ezért a követelményeket néhány helyen megváltoztatták. Ezeket a hibajavításokat és helyesbítéseket az 1122-es RFC részlete zi. A kiterjesztéseket a 1323-as RFC-ben írták le. Minden TCP-t támogató gép rendelkezik egy TCP szállítási entitással, amely lehet egy könyvtári eljárás, egy felhasználói folyamat vagy a kernel része. A TCP-folyamokat és az IP-réteg felé használható interfészeket minden esetben a TCP-entitás kezeli. A helyi folyamatoktól kapott felhasználói adatfolyamokat a TCP-entitás 64 KB-ot meg nem haladó méretű darabokra szedi szét (a gyakorlatban sokszor 1460 adatbájtos da rabokra, mert így az IP- és TCP-fejrészekkel együtt is beleférnek egy Ethernet-keret be). Az egyes darabokat önálló IP-datagramokban küldi el. Amikor egy géphez TCPadatokat tartalmazó datagram érkezik, az a TCP-entitáshoz kerül, amely visszaállítja az eredeti bájtfolyamokat. Az egyszerűség kedvéért néha olyankor is a „TCP" szót
579
A SZÁLLÍTÁSI RÉTEG
fogjuk használni, amikor a TCP-entitásról (ami egy szoftverelem) vagy a TCP-protokollról (ami egy szabálykészlet) beszélünk, a szövegkörnyezetből azonban mindig világos lesz, hogy melyikről van szó. Például, ha azt mondjuk, hogy „a felhasználó át adja a TCP-nek az adatokat", akkor nyilván a TCP szállítási entitásról van szó. Az IP-réteg nem ad garanciát a datagramok helyes kézbesítésére, így a TCP-re ma rad az időzítők kezelése és a szükség szerinti újraküldés. A helyesen megérkező datagramok is érkezhetnek rossz sorrendben, ezért a TCP felelőssége az is, hogy a datagramokat megfelelő sorrendben rakja össze üzenetekké. A fentieket röviden öszszefoglalva azt mondhatjuk, hogy a TCP-nek kell megteremtenie azt a megbízhatósá got, amelyet a legtöbb felhasználó megkíván, és amelyet az IP nem ad meg.
6.5.2.
A TCP szolgálati modell
A TCP-szolgálat úgy valósul meg, hogy mind a küldő, mind a fogadó létrehoz egy csatlakozónak (socket) nevezett végpontot, ahogy azt a 6.1.3. pontban tárgyaltuk. Minden csatlakozónak van egy száma, azaz csatlakozócíme, ami a hoszt IP-címéből és egy hoszton belül érvényes 16 bites számból, a port azonosítójából tevődik össze. A port a TCP-környezetben használt elnevezése a TSAP-nek. A TCP-szolgálat megvaló sításához egy közvetlen összeköttetést kell létesíteni a küldő és fogadó gép csatlakozói (socketjei) között. A csatlakozókat kezelő hívásokat a 6.5. ábrán foglaltuk össze. Egy csatlakozó egyidejűleg több összeköttetés kezelésére is használható, azaz két vagy több összeköttetés közös csatlakozóban is végződhet. Az összeköttetéseket a két végükön található csatlakozók azonosítói azonosítják: (socketl, socketl). Nem hasz nálnak virtuális áramkör sorszámot vagy más azonosítót. Az 1024 alatti portokat jól ismert portoknak (well-known port) hívják, és a meg szokott szolgáltatások részére vannak fenntartva. Például, bármely olyan folyamat, amely FTP-összeköttetést akar létrehozni egy állomány átviteléhez, a célgép 2l-es portján keresztül léphet kapcsolatba a gépen futó FTP-démonnal. A jól ismert portok listája megtalálható a www.iana.org-on. Eddig több mint 300 portot osztottak ki. A 6.27. ábra néhány ismertebbet sorol fel ezek közül. Port
Protokoll
Alkalmazás
21
FTP
Állományátvitel
23
Telnet
Távoli bejelentkezés
25
SMTP
E-levelezés
69
TFTP
Triviális állományátviteli protokoll
79
Finger
Tájékozódás más felhasználók adatairól Világháló (web)
80
HTTP
110
POP-3
Távoli e-levél hozzáférés
119
NNTP
USENET hírcsoportok
6.27. ábra. Néhány jól ismert port
580
SZÁMÍTÓGÉP-HÁLÓZATOK
Teljes mértékben lehetséges volna, hogy a gép indításakor az FTP-démon automa tikusan rákapcsolódjon a 2l-es portra, a telnet démon rákapcsolódjon a 23-as portra, és így tovább. Ezzel a megoldással azonban telezsúfolnánk a memóriát olyan démo nokkal, amelyek az idő jelentős részében tétlenek. A UNIX-ban ehelyett általában egyetlen, inetd-nek (Internet Daemon - Internet démon) nevezett démont alkal maznak. Az inetd egyszerre több portra kapcsolódik, és mindegyiken várja a bejövő összeköttetési kéréseket. Amikor kérés érkezik, az inetd egy új folyamatot indít el, amelyben lefuttatja a megfelelő démont, így az lekezelheti a kérést. Ezzel a megoldás sal elérhető, hogy (az inetd-t kivéve) minden démon csak akkor fusson, amikor mun kája is van. Az inetd a beállításait tartalmazó állományból tudja, hogy mely portokat kell figyelnie. Ez lehetővé teszi a rendszergazdának, hogy a legforgalmasabb portokra (pl. a 80-as port) állandó démonokat tegyen és az összes többit az inetd-re bízza. Minden TCP-összeköttetés duplex és kétpontos. A duplex azt jelenti, hogy a for galom egyszerre haladhat minkét irányba. A kétpontos azt jelenti, hogy minden öszszeköttetésnek pontosan két végpontja van. A TCP nem támogatja az adatszórást és a többesküldést. A TCP-összeköttetésen nem üzenetfolyamok, hanem bájtfolyamok áramlanak. Az üzenethatárokat a rendszer nem őrzi meg. Például, ha a küldő folyamat négy 512 bájtos írást hajt végre a TCP-folyamon, az a vevőhöz megérkezhet négy 512 bájtos da rabban, két 1024 bájtos darabban, egy 2048 bájtos darabban (lásd 6.28. ábra) vagy akár más felosztásban is. A vevőnek nem áll módjában kideríteni, hogy az adatokat mekkora darab(ok)ban küldték. A UNIX-os állományok is rendelkeznek ezzel a tulajdonsággal. Az állomány olva sója nem tudja megállapítani, hogy az állományt blokkonként, bájtonként vagy egyetlen darabban írták-e. A UNIX állománykezeléséhez hasonlóan a TCP-szoftver sem tud semmit a bájtok jelentéséről és azt kitalálni sem próbálja. A TCP számára egy bájt csak egy bájt. Amikor egy alkalmazás adatot ad át a TCP-nek, a TCP dönti el, hogy azonnal el küldi azt vagy puffereli egy ideig, hogy így nagyobb adatmennyiséget küldhessen el egyszerre. Az alkalmazásnak azonban néha fontos, hogy az adatokat a TCP azonnal elküldje. Tegyük fel például, hogy a felhasználó egy távoli gépre jelentkezett be. Rendkívül fontos, hogy a felhasználó által begépelt és kocsivissza-karakterrel lezárt parancssorokat a TCP azonnal továbbítsa, és ne pufferelje addig, amíg a következő sor meg nem érkezik. Az alkalmazások a PUSH jelzőbitet használhatják az azonnali adat küldés kikényszerítésére. A PUSH bit arra utasítja a TCP-t, hogy ne késlekedjen az adatok elküldésével. IP-fejrész
*'/ */ A
e;
TCP-fejrész
/ B /
£
A B C D (b)
6.28. ábra. (a) Négy 512 bájtos szegmens, amelyeket az adó külön IP-datagramokban küld el. (b) A 2048 adatbájtot a vevő alkalmazási folyamat egyetlen REÁD hívásban kapja meg.
A SZÁLLÍTÁSI RÉTEG
581
Néhány korai alkalmazás a PUSH bitet üzenethatárok jelzésére használta. Habár van, amikor működik ez a trükk, néha kudarcot vall, mivel nem minden TCP-implementáció adja át a PUSH bitet a vevőoldali alkalmazásnak. Ezenkívül, ha az első PUSH elküldése előtt újabbak érkeznek (például ha foglalt a kimenő vonal), a TCPnek jogában áll az összes PUSH bittel jelzett adatot egyetlen IP-datagramba összefog ni, ahol az egyes darabok már nem lesznek különválasztva. A TCP utolsó, említésre érdemes szolgálata a sürgős adat (urgent data). Amikor egy interaktív felhasználó DEL-t vagy CTRL-C-t üt egy már megkezdett távoli számítás megszakítására, a küldő alkalmazás további vezérlőinformációt ad az adatfolyamhoz, és sürgős jelzéssel átadja a TCP-nek. Ennek hatására a TCP abbahagyja az adatok egybe gyűjtését az adott összeköttetésre, és azonnal továbbítja az eddig összegyűlt adatot. Amikor a sürgős adat célba ér, a vevő alkalmazás végrehajtása megszakad (UNIX terminológiában signal-t kap), abbahagyja, amit éppen csinált, és beolvassa az adatfo lyamot, hogy megtalálja a sürgős adatot. A sürgős adat vége jelezve van, így az alkal mazás tudja, hogy meddig tart. A sürgős adat kezdetén ezzel ellentétben nincs jelzés, azt az alkalmazásnak kell megtalálnia. Ez a megoldás csak egy kezdetleges jelzési rendszert biztosít, és minden más feladatot az alkalmazásra hagy.
6.5.3.
A TCP-protokoll
Ebben a szakaszban a TCP-protokollt fogjuk nagy vonalakban áttekinteni. A követke ző szakaszban a protokoll fejrészét fogjuk mezőnként megvizsgálni. A TCP rendelkezik egy olyan, kulcsfontosságú tulajdonsággal, amely az egész protokoll kialakítását befolyásolja: a TCP-összeköttetéseken minden bájt rendelkezik egy 32 bites sorszámmal. Amikor az Internet története elkezdődött, a routereket öszszekötő vonalak általában 56 kb/s-os bérelt vonalak voltak, ezért egy teljes sebesség gel adó hoszt esetében is több, mint egy hét eltelt a sorszámozás két újrakezdése kö zött. Később látni fogjuk, hogy a mai hálózati sebességek mellett a hosztok a sorszá mokat ijesztő sebességgel fogyaszthatják. A nyugták és a csúszóablakok ettől függet len 32 bites sorszámokat használnak, amint az a következőkből is kiderül. A küldő és a vevő TCP-entitások az adatokat szegmensekben viszik át egymás kö zött. A TCP-szegmensek (TCP segment) egy rögzítetten 20 bájtos fejrészből (és egy nem kötelező, opcionális részből), valamint 0 vagy több adatbájtból állnak. A TCPszoftver dönti el, hogy a szegmensek mekkorák legyenek. Összegyűjtheti több írási utasítás adatait egyetlen szegmensbe, de egy írás adatait is eloszthatja több szegmens be. A szegmensek méretének két korlátja van. Egyrészt, minden szegmensnek a hozzá tartozó TCP-fejrésszel együtt bele kell férnie a 65 515 bájtos IP-adatmezőbe. Más részt, minden hálózaton meghatározzák az úgynevezett legnagyobb átvihető adat egységet (Maximum Transfer Unit - MTU), amelybe minden szegmensnek bele kell férnie. A gyakorlatban az MTU, vagyis a szegmensméret felső korlátja általában 1500 bájt (az Ethernet-adatmező mérete). A TCP-entitások egy csúszóablakos protokoll-változatot használnak. A küldő min den szegmens feladásakor egy időzítőt is elindít. Amikor a szegmens megérkezik a célhoz, a vevő TCP-entitás visszaküld egy olyan szegmenst (adatokkal tele, ha van el-
582
SZÁMÍTÓGÉP-HÁLÓZATOK
küldendő adat, egyébként üresen), amelyben a következőnek várt szegmens sorszá mával visszaigazolja az adást. Ha a küldő oldalon az időzítő a nyugta vétele előtt lejár, akkor a küldő entitás újra elküldi a szegmenst. Annak ellenére, hogy ez a protokoll egyszerűnek hangzik, van néhány, esetenként apró buktatója, amelyeket alább fogunk megtárgyalni. A szegmensek érkezhetnek helytelen sorrendben, így előfordulhat, hogy a 3072-4095 sorszámú bájtok megérkez nek, de a vevő nem nyugtázhatja azokat, mivel a 2048-3071 sorszámú bájtok még nem érkeztek meg. Előfordulhat, hogy a szegmensek olyan hosszú ideig utaznak, hogy a küldő időzítője közben lejár és újra elküldi azokat. Előfordulhat az is, hogy az újraküldés során az adó nem ugyanazokat a bájt-tartományokat küldi el az egyes szegmensekben, ezért gondos nyilvántartásra van szükség ahhoz, hogy a TCP-entitás nyomon követhesse az adott pillanatig helyesen vett bájtokat. Ez azért kivitelezhető megoldás, mert a folyam minden bájtja egyedi eltolással (offset) rendelkezik. A TCP-nek felkészülten kell szembenéznie ezekkel a problémákkal, és hatékony módon kell megoldania azokat. A tervezők jelentős mennyiségű erőfeszítést fordítot tak arra, hogy a TCP-folyamok teljesítménye még hálózati gondok esetén is optimális legyen. A későbbiekben ismertetni fogunk néhány olyan algoritmust, amelyet sok TCP-megvalósítás használ.
6.5.4.
A TCP-szegmens fejrésze
A 6.29. ábrán láthatjuk a TCP-szegmens felépítését. Minden szegmens egy fix kiosz tású 20 bájtos fejrésszel kezdődik, amit fejrészopciók követhetnek. Ezek után - ha van - legfeljebb 65 535 - 20 - 20 = 65 495 bájt adat állhat. A kivonandók közül az első 20 bájt az IP-, a második 20 bájt a TCP-fejrészt jelenti. Az adatot nem tartalmazó szeg mensek érvényesek, általában nyugtázásra és vezérlő szegmensként használják őket. Elemezzük most mezőről mezőre a TCP-fejrészt! A Forrásport és Célport mezők azonosítják az összeköttetés helyi végpontjait. Az egyes hosztok a www.iana.org-on definiált jól ismert portok kivételével bárhogyan kioszthatják a portjaikat. A portszám és a hoszt IP-címe együtt egy egyedi 48 bites azonosítót jelent a végpontok megkü lönböztetésére. A forrás és a cél végpontok együtt azonosítják az összeköttetéseket. A sorszám (sequence number) és nyugtaszám (acknowledgement number) mezők szerepe szokásos. Jegyezzük meg, hogy az utóbbi a következő várt bájt sorszámát tar talmazza, nem az utolsó rendben beérkezett bájtét. Mindkét mező 32 bit széles, mivel a TCP-folyamban minden adatbájt sorszámot visel. A TCP-fejrészhossz (TCP header length) mondja meg, hány 32 bites szóból áll a TCP-fejrész. Ez az információ azért szükséges, mert a fejrész mérete az opciók (options) mező változó hossza miatt szintén változó. Tulajdonképpen ez a mező jelzi az adat kezdetét (32 bites szavakban mérve) a szegmensen belül, de mivel ez egyben a fejrész szavakban mért hossza is, a végeredmény ugyanaz. Ezután egy használaton kívüli 6 bites mező következik. A TCP jól átgondolt terve zésére szolgál tanúbizonyságul ezen mező több mint egy évtizedes változatlan állapo ta. Kevésbé jól sikerült protokollokban már fölhasználták volna az eredeti változat hi báinak kiküszöbölésére.
583
A SZÁLLÍTÁSI RÉTEG
32 bit i
i
i
i
i
I
i
i
i
i — i — i — i — I — i
i
i
i
i
i
Forrásport
i
l
i
i
i
i
i
i
i
Célport Sorszám Nyugta
TCP fejrész hossz
Ablakméret Ellenőrzőösszeg
T
Sürgősségi mutató
Opciók (0 vagy több 32 bites szó) Adatok (opcionális)
6.29. ábra. A TCP-fejrész Ezt hat egybites mező követi. Az URG bit értéke 1, ha sürgősségi mutatót használt. A sürgősségi mutató a sürgős adat bájtban mért helyét jelzi a jelenlegi bájtsorszámhoz vi szonyítva. Ez a mechanizmus a megszakítás üzeneteket helyettesíti. Mint korábban emlí tettük, ez a végsőkig leegyszerűsített módszer lehetőséget ad a küldőnek, hogy jelzést küld jön a vevő felé anélkül, hogy a TCP-nek külön megszakításokkal kelljen foglalkoznia. Az ACK bit 1 értéke jelzi a nyugta mező érvényességét. Ha ACK= 0, a szegmens nem tartalmaz nyugtát, tehát a nyugta mező figyelmen kívül hagyható. A PSH bit jelzi a késedelem nélküli adat továbbítását (PUSH). Ez egyben a vevő felé is udvarias kérést jelent: ne pufferelje a vett adatot (amit amúgy hatékonysági okokból megtehetne), hanem azonnal továbbítsa az alkalmazás felé. Az RST bit egy hoszt összeomlása, vagy más okból összezavart összeköttetés hely reállítására szolgál. Ezenkívül érvénytelen szegmens elutasítására és összeköttetés lé tesítésének megtagadására is használják. Rendszerint ha valaki RST = 1 értéket viselő szegmenst kap, akkor felkészülhet valamilyen probléma megoldására. A SFNbit összeköttetés létesítésére szolgál. Az összeköttetés-kérésben SYN= 1 és ACK= 0 jelzi, hogy ráültetett nyugta mezőt nem használnak. Az összeköttetés-kérés sel adott válaszban már van nyugta, így abban SYN- 1 és ACK- 1. Lényegében a SYN bit jelzi a CONNECTION REQUEST és CONNECTION ACCEPTED üzeneteket, melyeket az ACK bit különbözteti meg egymástól. A FIN bit szolgál egy összeköttetés bontására. Jelzi, hogy a küldőnek nincs több továbbítandó adata. Az összeköttetés bontása után azonban még korlátlan ideig foly tathatja az adatok vételét. Mind a SYN, mind a FIN szegmensek rendelkeznek sorszá mokkal, így garantált a helyes sorrendben történő feldolgozás.
584
SZÁMÍTÓGÉP-HÁLÓZATOK
A TCP forgalomszabályozása változó méretű csúszóablakkal történik. Az ablak méret mező határozza meg, hogy a nyugtázott bájttal kezdődően hány bájtot lehet el küldeni. Az ablakméret 0 értéke is érvényes, és azt jelzi, hogy a nyugtázott bájtnál eggyel kisebb sorszámú bájtok mind rendben megérkeztek, viszont a vevőnek nagy szüksége van egy kis pihenésre és köszöni szépen, több adatot jelenleg nem kér. Ké sőbb azonos nyugta értékkel rendelkező, és nem nulla ablakméret mezővel ellátott szegmenssel engedélyezni lehet az adatok küldését. A 3. fejezet protokolljaiban a vett keretek nyugtázása és új kretek küldésének en gedélyezése szorosan összefonódott egymással. Ez annak a következménye, hogy az egyes protokollokban használt ablakok mérete rögzített volt. A TCP-ben a nyugtázás teljesen szétválik a további adatküldés engedélyezésétől. A vevő tulajdonképpen azt is mondhatja, hogy „k-ig megkaptam a bájtokat, de most egyelőre nem kérek többet". Ez a szétválasztás (amely gyakorlatilag egy változó méretű ablakot jelent) megnöveli a rendszer rugalmasságát, ezért később részletes vizsgálat alá vesszük. A nagyobb megbízhatóság érdekében van még egy ellenőrző összeg is a fejrészben. Ez ellenőrzi a fejrész, az adat és a 6.30. ábrán látható pszeudofejrész épségét. Ennek számításakor a TCP ellenőrző összeg mezeje 0 értéket kap, és az adatmező egy továb bi bájttal bővül, ha a hossza eddig páratlan szám volt. Az ellenőrző összeg számítása egyszerűen a 16 bites szavak l-es komplemens összeadásával történik, majd az összeg l-es komplemensét vesszük. Ennek következményeként, amikor a vevő az egész szeg mensre - beleértve az ellenőrző összeg mezőt is - elvégzi ugyanezt a számítást, az eredménynek 0-nak kell lennie. A pszeudofejrész a forrás- és célhosztok 32 bites IP-címét, a TCP-protokoll azono sítóját (6), és a TCP-szegmens (fejrésszel együtt mért) hosszát tartalmazza. Azzal, hogy a pszeudofejrész is részt vesz az ellenőrző összeg képzésében, a tévesen továbbí tott csomagok könnyebben detektálhatok, de ez a protokollhierarchiát is sérti, mert az itt szereplő IP-címek nem a TCP-réteghez, hanem az IP-hez tartoznak. Az opciók mezőt a szabályos fejrészben nem szereplő lehetőségek megvalósítására tervezték. A legfontosabb lehetőség lehetővé teszi a hosztoknak, hogy meghatározzák a legnagyobb általuk elfogadható TCP-adatmező nagyságát. Nagy szegmensek hasz nálata hatékonyabb kicsik alkalmazásánál, hiszen a 20 bájtos fejrész több adathoz tar tozik, viszont kis hosztok esetleg képtelenek nagyon nagy szegmensek kezelésére. Összeköttetés létesítésekor minden hoszt megadhatja a saját maximumát, és tudomást 32 bit J
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
i
Forráscím Rendeltetési cím 00000000
Protokoll azonosító PID = 6
TCP szegmens hossza
6.30. ábra. A TCP ellenőrző összeg képzésében szerepelő pszeudofejrész
i
i
i
A SZÁLLÍTÁSI RÉTEG
585
szerezhet a partnere maximum értékéről. Ha egy hoszt nem használja ezt a lehetősé get, az alapértelmezés 536 bájtos adat mező. Minden Internetre csatlakozó hosztnak el kell fogadnia 536 + 20 = 556 bájt hosszúságú szegmenseket, viszont a két irányban nem kell azonosnak lenni a maximális hosszaknak. Nagy sávszélességgel vagy nagy késleltetéssel, esetleg mindkettővel rendelkező vo nalak számára gyakran problémát jelent a 64 KB ablakméret. Egy T3 vonalon (44,736 Mb/s) csak 12 ms ideig tart egy 64 kilobájtos tele ablak továbbítása. Ha a teljes átviteli késleltetés 50 ms (tipikus kontinensek közötti fényvezető szálakra), a küldő a várakozási idő 3/4 részében tétlenül várakozik a nyugtára. Egy műholdas összeköttetésen még ennél is rosszabb a helyzet. Nagyobb ablakméret használata esetén a küldő tovább pumpálhat ná az adatot, de 16 bites ablakméret mezővel nem lehet akkora méretet kifejezni. Meg oldásul az RFC 1323-ban az ablak skálatényező (window scale) opció használatát java solták, amely lehetővé tenné a küldő és a fogadó számára, hogy megegyezzenek egy ablak-skálatényezőben. Ez a szám mindkét oldalon lehetővé teszi az ablakméret mező legfeljebb 14 bites balra történő eltolását, így legfeljebb 230 bájtos ablakok használatát. A legtöbb TCP-implementáció ma már támogatja ezt a lehetőséget. Egy másik, az RFC 1106-ban javasolt, és most már széles körben megvalósított opció a szelektív ismétlés, melyet az n visszalépéses protokoll helyett alkalmaznak. Ha a vevő kap egy rossz szegmenst, majd nagyszámú jó érkezik, a normális TCP-protokoll szerint működő küldő időzítése végül lejár, és minden nyugtázatlan szegmenst újraküld, még azokat is, amelyek rendben megérkeztek. Az RFC 1106-ban bevezettek NAK-okat, hogy lehetővé tegyék a vevőnek egy adott szegmens (vagy szegmensek) újraátviteli kérését. Miután ezek megérkeztek, az összes puffereit adatot nyugtázza. Ezzel a mechanizmussal csökkenthető az újraküldött adatok mennyisége.
6.5.5.
TCP-összeköttetés létesítése
Az összeköttetések létesítésére a 6.2.2. szakaszban tárgyalt háromutas kézfogás tech nikát alkalmazzák a TCP-ben. Az összeköttetés létesítéséhez az egyik fél, nevezzük szervernek, passzívan várakozik a bejövő kérésekre a LISTEN és ACCEPT primitívek végrehajtásával. Ehhez megjelölhet egy adott forrást, vagy nem jelöl ki senkit. A másik oldal, melyet kliensnek hívunk, végrehajtja a CONNECT primitívet, mely nek hívásakor rögzíti az IP-címet, a használni kívánt port számát, az általa megenge dett maximális TCP-szegmens méretét és esetleg felhasználói adatot (pl. jelszót) is át ad. A CONNECT primitív elküld egy TCP-szegmenst SYN - 1 és ACK=0 értékkel, majd választ vár. Amikor a szegmens megérkezik a rendeltetési helyre, az ottani TCP-entitás ellenőrzi, hogy létezik-e egy folyamat, amely a célport mezőben meghatározott porton végrehaj totta a LISTEN primitívet. Ha nem, RST- 1 válasszal elutasítja az összeköttetés-kérést. Ha valamilyen folyamat figyeli a megadott portot, akkor az megkapja a beérkező TCP-szegmenst, és rajta múlik, hogy elfogadja-e vagy visszautasítja az összeköttetést. Ha elfogadja, egy nyugtázó szegmenst küld vissza. A 6.31.(a) ábrán láthatjuk az elkül dött TCP-szegmensek sorozatát normális esetben. Figyeljük meg, hogy a SYN szeg mens egy bájtnak megfelelő sorszámot fogyaszt, így egyértelműen nyugtázható.
586
SZÁMÍTÓGÉP-HÁLÓZATOK
1. hoszt
2. hoszt
(a) 1. hoszt
2. hoszt
6.31. ábra. (a) TCP-összeköttetés létesítés normális esetben, (b) Hívások ütközése Abban az esetben, ha a két hoszt egyszerre próbál összeköttetést létesíteni ugyan azon két csatlakozó (socket) között, a 6.31.(b) ábrán látható eseménysorozat alakul ki. Ennek eredményeképpen csak egy összeköttetés jön létre kettő helyett, mivel az öszszeköttetéseket végpontjaik azonosítják. Ha a művelet során létrejövő' első összekötte tés azonosítói (x, v), és a második is ilyen azonosítóval születik meg, csak egy (x, y) azonosítójú bejegyzés keletkezik a táblázatban. Az összeköttetés kezdő sorszáma korábban már tárgyalt okokból kifolyólag nem 0. Órára alapozott módszert alkalmaznak, melyben az óra 4 us-onként kettyen. A bizton ság növelése érdekében, ha egy hoszt összeomlik, a maximális csomag élettartamig nem indulhat újra. így biztosítható, hogy a korábbi összeköttetésekből származó cso magok már nem bolyonganak szerte az Internetben, amikor újra működésbe lép.
6.5.6.
TCP-összeköttetés lebontása
Bár a TCP-összeköttetések duplexek, hogy megérthessük az összeköttetések bontásá nak módját, legjobb két szimplex összeköttetésnek tekinteni azokat. Mindkét szimplex összeköttetés a másiktól függetlenül lebontható. Egy összeköttetés bontásához bárme-
587
A SZÁLLÍTÁSI RÉTEG
lyik fél küldhet egy FIN - 1 értékű TCP-szegmenst, amivel jelzi, hogy nem szándéko zik több adatot küldeni. Amint a FIN nyugtája megérkezik, az az irány lezárul. A má sik irányban azonban ettől függetlenül korlátlanul folyhat adatátvitel. Amikor mindkét irányt lezárták, az összeköttetés befejeződött. Normális esetben egy összeköttetés bontásához négy TCP-szegmens szükséges, egy FIN és egy ACK mindkét irányban. Az első ACK és a második FIN viszont elhelyezhető ugyanabban a szegmensben is, így összesen csak háromra van szükség. Hasonlóan a telefonhívásokhoz, ahol mindkét ember egyszerre köszön el és teszi le a kagylót, itt is küldhet mindkét fél egy időben FIN szegmenst. Ezeket a szokásos mó don nyugtázzák, majd az összeköttetés befejeződik. Valójában nincs lényeges eltérés az egyszerre vagy egymás után történő összeköttetés-bontások között. A két-hadsereg probléma elkerülésére időzítőket használnak. Ha egy F/TV-re nem érkezik válasz két csomag-élettartamnyi idő alatt, a F/N küldője bontja az összekötte tést. A másik fél végül észreveszi, hogy már senki sem figyel rá, az általa indított idő zítő is lejár. Bár ez a megoldás nem tökéletes, az tény, hogy tökéletes megoldás elmé letben sem létezhet, ezért ennek elégnek kell lenni. A gyakorlatban ritkán merülnek föl problémák.
6.5.7.
TCP-összeköttetés-kezelés modellje
Összeköttetések létesítését és bontását véges automatával is modellezhetjük. A gép 11 állapotát a 6.32. ábrán láthatjuk. Minden állapotban az események bizonyos halmaza érvényes. Ha érvényes esemény történik, lejátszódik valamilyen tevékenység, más esemény hatására hibajelzés keletkezik. Minden összeköttetés CLOSED állapotból indul. Akkor hagyja el ezt az állapotot, amikor passzív módon (LISTEN) vagy aktív módon (CONNECT) összeköttetést próbál létesíteni. Ha a másik fél az ellenkező primitívet hajtja végre, az összeköttetés felépül és ESTABLISHED állapotot vesz fel. Bármelyik fél kezdeményezheti az összeköttetés bontását. Amikor ez lezajlott, az állapot ismét CLOSED lesz. Állapot CLOSED LISTEN SYN RCVD SYN SENT ESTABLISHED FIN WAIT 1 FIN WAIT 2 TIMED WAIT CLOSING CLOSE WAIT LAST ACK
Leírás Nincs aktív vagy függő összeköttetés A szerver egy hívás beérkezésére vár Összeköttetés-kérés érkezett, ACK-ra vár Az alkalmazás összeköttetés-létesítést kezdeményezett Normális adatátviteli állapot Az alkalmazás bejelentette, hogy végzett teendőivel A másik fél beleegyezett az összeköttetés bontásába Vár, amíg az összes csomag ki nem hal Mindkét fél egyszerre próbálta bontani az összeköttetést A másik fél bontást kezdeményezett Vár, amíg az összes csomag ki nem hal
6.32. ábra. A TCP-összeköttetéseket kezelő véges állapotú gép állapotai
588
SZÁMÍTÓGÉP-HÁLÓZATOK
Magát a véges állapotú gépet a 6.33. ábrán láthatjuk. Vastag vonalak mutatják egy aktív kliens összeköttetés-létesítési folyamatát egy passzív szerverrel, a folytonos vo nalak a kliensre, a szaggatottak a szerverre vonatkoznak. A vékony vonalak váratlan eseményeket jeleznek. A 6.33. ábrán az összes nyílhoz tartozik egy esemény/tevékeny ség pár. Az esemény lehet egy felhasználó által végrehajtott rendszerhívás (CONNECT, LISTEN, SEND vagy CLOSE), egy szegmens érkezése {SYN, FIN, ACK vagy RST), vagy (Kezdőállapot)
CONNECT/SYN (a 3-utas kézfogás 1. lépése)
CLOSED CLOSE/LISTEN/CLOSE/SYN/SYN + ACK (a 3-utas kézfogás 2. lépése) LISTEN
7
RST/-
SYN RCVD
SEND/SYN
SYN/SYN + ACK
SYN SENT
(Egyidejű létesítés)
(Adatátvitelre használható állapot) ACK/-
ESTABLISHED
CLOSE/FIN
r FIN |WAIT1
CLOSE/FIN
FIN/ACK
(Aktív bontás)
(Passzív bontás) ;
•
FIN/ACK CLOSING
ACK/ FIN WAIT2
SYN + ACK/ACK (A 3-utas kézfogás 3. lépése)
CLOSE WAIT CLOSE7FIN
ACK/-
VQN + ACK/ACK FIN/ACK
' TIME WAIT
LAST ACK 1 1 1
(Időtúllépés/) ACK7CLOSED (Vissza a kezdőállapotba) 6.33. ábra. A TCP-ósszeköttetés-kezelés véges állapotú gépe. A vastag folytonos vonalak a kliens szokásos állapotátmenetei. A vastag szaggatott vonalak a szerver szokásos állapotátmenetei. A vékony vonalak a rendkívüli események. Minden átmenet címkéje az átmenetet kiváltó esemény és az átmenet által okozott tevékenység, „/"jellel elválasztva
A SZÁLLÍTÁSI RÉTEG
589
egy esetben a kétszeres maximális csomagélettartamra beállított időzítő lejárása. A te vékenység lehet vagy egy vezérlő szegmens (SYN, FIN vagy RST*) elküldése, vagy semmi (ezt „-"jelöli). A megjegyzések zárójelben láthatók. Az ábrát úgy a legkönnyebb megérteni, ha először végigkövetjük a kliens állapot átmeneteit (a vastag folytonos vonalakat), majd a szerver állapotátmeneteit (a vastag szaggatott vonalakat) is sorra végignézzük. Amikor az egyik alkalmazási program a kliensgépen kiad egy CONNECT kérést, a helyi TCP-entitás létrehoz egy új összekötte tés-rekordot, amelyet SYN SENT állapotúnak jelöl meg és elküld egy SYN szegmenst. Fontos megjegyezni, hogy egyszerre több összeköttetés felépítése, illetve fenntartása lehet folyamatban, több különböző alkalmazás részére. Az állapotok emiatt különkülön tartoznak az egyes összeköttetésekhez és a TCP-entitás az összeköttetés-rekor dokban tartja nyilván azokat. Amikor a SYN + ACK megérkezik, akkor a TCP elküldi a háromutas kézfogás utolsó ACK szegmensét és az ESTABL1SHED állapotba viszi át az összeköttetést. Ezután szabadon lehet adatokat küldeni és fogadni. Amikor egy alkalmazás befejezte tevékenységét, végrehajtja a CLOSE primitívet, melynek hatására a helyi TCP-entitás egy FIN szegmenst küld, majd az ehhez tartozó nyugtára (ACK) vár (szaggatott téglalap aktív lebontás megjegyzéssel). Amikor meg érkezik az ACK, az új állapot FIN WAIT2 lesz, és az összeköttetés egyik irányban be fejeződik. Amikor a másik fél is bont, egy FIN szegmens érkezik, amire a helyi entitás nyugtát küld. Ekkorra mindkét fél lebontotta az összeköttetést, de a TCP még maxi mális csomagélettartam ideig várakozik, így még akkor is garantálható, hogy az öszszeköttetés összes csomagja kihal, ha esetleg egy nyugta elveszett volna. Amikor az időzítő lejár, a TCP törli az összeköttetés bejegyzését. Most vizsgáljuk meg az összeköttetés kezelését a szerver szemszögéből. A szerver LISTEN hívást ad ki és csöndben figyeli, hogy ki bukkan föl. Amikor beérkezik egy SYN szegmens, nyugtázza, és SYN RCVD állapotba lép. Amint a szerver SYN szeg mensére is nyugta érkezik, a háromutas kézfogás protokoll véget ér és a szerver ESTABLISHED állapotba kerül. Megkezdődhet az adatátvitel. Ha a kliens végzett tennivalóival, CLOSE hívást ad ki, melynek hatására FIN szeg mens érkezik a szerverhez (szaggatott vonalas téglalap passzív lebontás felirattal). A szerver ezután megszakítást (signal) kezdeményez. Amikor a szerver is végrehajtja a CLOSE primitívet, a TCP-entitás FIN szegmenst küld a kliensnek. Amint a kliens nyugtá ja megjelenik, a szerver bontja az összeköttetést és törli a hozzá tartozó bejegyzést.
6.5.8.
TCP átviteli politika
Amint azt már korábban is említettük, a TCP-ben az ablakkezelés nem kötődik olyan szorosan a nyugtázáshoz, mint a legtöbb adatkapcsolati protokollban. Tegyük fel pél dául, hogy a vevő 4096 bájtos pufferrel rendelkezik (6.34. ábra). Ha a küldő egy 2048 bájtos szegmenst küld el, ami rendben meg is érkezik, a vevő nyugtázza a vételt. Mi vel azonban most csak 2048 bájt szabad pufferterülete van (amíg az alkalmazás ki nem olvas belőle valamennyi adatot) bejelenti, hogy a következő bájttól kezdve 2048 bájtos ablakot használ.
590
SZÁMÍTÓGÉP-HÁLÓZATOK
Küldő Az alkalmazás 2 K-t ír
Vevő
Vevő puffere 0
4K Üres
2K Az alkalmazás 2 K-t ír Tele A küldő blokkolódott
Az alkalmazás 2 K-t olvas 2K
A küldő legfeljebb 2 K-i küldhet
1K
2K
6.34. ábra. A TCP ablakkezelése Most a küldő újabb 2048 bájtot továbbít, amit a vevő nyugtáz, továbbá közli, hogy az ablakméret 0 bájt. A küldőnek le kell állnia, amíg a fogadó hoszton futó alkalmazói folyamat el nem távolít valamennyi adatot a pufferből, amikor is a TCP egy nagyobb ablak használatát jelentheti be. Amikor az ablakméret 0 bájt, a küldő normális esetben nem küldhet szegmenst, azonban van két kivétel. Először is a sürgős adatot továbbíthatja, hogy lehetővé tegye például a távoli gépen futó folyamat megszakítását. Másodszor, a küldő eíküidhet egy egybájtos szegmenst, melyben arra kéri a vevőt, hogy közölje vele a következő várt bájt sorszámát és az ablakméretet. A TCP-szabvány explicit módon biztosítja ezt a le hetőséget, hogy elkerülje a holtpontot, amennyiben egy ablakméretet közlő szegmens elveszne. A küldő nem köteles azonnal továbbítani az alkalmazástól kapott adatot. A vevő sem köteles azonnal nyugtázni a beérkezett szegmenseket. Például a 6.34. ábrát tekint ve, amikor beérkezett az első 2 KB adat, a TCP, tudva, hogy 4 KB-os ablak áll rendel kezésére, teljesen korrekt módon járna el, ha az adatot újabb 2 KB beérkezéséig a pufferben tárolná, hogy 4 KB rakománnyal tudja továbbítani a szegmenst. Ezt a szabad ságot a teljesítőképesség fokozásában lehet kamatoztatni.
A SZÁLLÍTÁSI RÉTEG
591
Vegyünk egy TELNET összeköttetést egy interaktív szövegszerkesztővel, ami minden billentyűleütésre reagál. Legrosszabb esetben, amikor megérkezik egy karak ter a küldő TCP-entitáshoz, az létrehoz egy 21 bájtos szegmenst, amit átad az IP-nek, hogy 41 bájtos IP-datagramként továbbítsa. A vevő oldali TCP azonnal visszaküld egy 40 bájtos nyugtát (20 bájtnyi TCP-fejrész és újabb 20 bájtnyi IP-fejrész). Később, mikor a szövegszerkesztő beolvasta a kapott bájtot, a TCP egy bájttal jobbra mozdítja az ablakot, és ezt közli a küldővel is. Ez a csomag szintén 40 bájtos. Végül, mikor a szövegszerkesztő feldolgozta a karaktert, egy 41 bájtos csomagban értesíti a feladót. Összesen négy szegmens továbbítása, azaz a sávszélesség 162 bájtja szükséges min den egyes begépelt karakterhez. Amikor a sávszélesség az igényeltnél kisebb, nem célszerű így gazdálkodni. A fenti helyzet optimalizálására több TCP-implementációban alkalmazzák a követ kező megközelítést. Késleltessük a nyugták és ablakméret információk elküldését 500 ms-ig azt remélve, hogy egy visszaküldendő adatcsomagba ágyazva ingyen elküld hetjük őket. Ha feltételezzük, hogy a szövegszerkesztő fél másodpercen belül küld visszajelzést, csak egyetlen 41 bájtos csomagot kell elküldeni a távoli felhasználónak. Az elküldött csomagok száma és a fölhasznált sávszélesség így a felére csökken. Bár ez a szabály csökkenti a hálózat vevő által okozott terhelését, a küldő az egyet len adatbájtot tartalmazó 41 bájtos csomagok küldözgetésével még mindig pazarlóan gazdálkodik. Az ennek csökkentésére kidolgozott módszer a Nagle-féle algoritmus (Nagle, 1984) néven ismert. Nagle ötlete egyszerű: amikor a küldőhöz bájtonként ér kezik az adat, csak az elsőt továbbítja, a többit addig puffereli, amíg az elküldött bájt nyugtája meg nem érkezik. Ezután a pufférben tárolt összes karaktert egyetlen TCPszegmensben elküldi, és újra kezdi a pufferelést, amíg az összes nyugta meg nem ér kezett. Ha a felhasználó gyorsan gépel, és lassú a hálózat, minden szegmensben szá mos karakter utazhat, jelentősen csökkentve a felhasznált sávszélességet. Az algorit mus ezenkívül lehetővé teszi egy újabb csomag küldését, ha fél ablakra való, vagy a maximális szegmensméretet kiadó adat összegyűlt. A Nagle-féle algoritmus széles körben elterjedt a TCP-implementációkban, de né mely esetben szerencsésebb kikapcsolni. Például amikor egy X-Window alkalmazás fut az Internet felett, az egérmozgásokat el kell küldeni a távoli számítógépnek. Ha összegyűjtenénk őket, és löketszerűen továbbítanánk, az egérmutató ugrálva mozog na, ami bosszantaná a felhasználókat. Egy másik probléma, ami le tudja rontani a TCP teljesítőképességét, a buta ablak jelenség (süly window syndrome) (Clark, 1982). Ez a probléma akkor merül föl, amikor a küldő TCP-entitás nagy blokkokban kapja az adatokat, de a fogadó oldalon futó interaktív alkalmazás bájtonként olvassa be. A probléma könnyebb megértésére vegyük szemügyre a 6.35. ábrát. Kezdetben a vevő TCP-puffere tele van, és a küldő ezzel tisztában van (tehát az ablakmérete 0). Ezután az interaktív alkalmazás beolvas egy bájtot a TCP-adatfolyamról. Megörül ennek a fogadó TCP-entitás, elküldi az új ablakméretet a küldőnek, mondván, hogy minden rendben, egy bájtot küldhet. A küldő teljesíti ezt a kívánságot is, egy bájtot elküld. A puffer ismét tele lesz, így a vevő nyugtázza a bájt érkezését, de egyidejűleg közli, hogy az ablak mérete 0. Ez a viselkedés így mehet örökké. Clark megoldása szerint nem szabad megengedni a vevőnek, hogy 1 bájt változásra
592
SZÁMÍTÓGÉP-HÁLÓZATOK
i A vevő puffere tele van
;
i
I
Az alkalmazás 1 bájtot beolvas — Még 1 bájt számára van hely [ Fejrész
Ablakfrissítés szegmens küldése
Fejrész / 1 bájt
. I l
Új bájt érkezik A vevő puffere tele van
l
J
6.35. ábra. A buta ablak jelenség ablakméret frissítést küldjön. Ehelyett várakoztatni kell addig, amíg elegendő' hely szabaddá nem válik, és inkább azt kell a küldővel közölni. Pontosabban a fogadó nem küldhet addig ablakméret információt, amíg az összeköttetés létesítésekor bejelentett maximális szegmensméretet nem tudja kezelni, vagy félig ki nem ürült a puffer. A két korlát közül a kisebbet kell elérnie a szabad hely méretének. Ezenkívül a küldő is segíthet azzal, hogy nem küld apró szegmenseket. Ehelyett, addig el kell halasztani a továbbítást, amíg elég hely össze nem gyűlt az ablakban, hogy egy teljes szegmenst elküldhessen, vagy legalább olyan hosszút, mint a vevő ablakméretének fele (amit az eddig beérkezett ablakméret információk alapján becsül het meg). A Nagle-féle algoritmus és Clark megoldása a buta ablak problémára kiegészítik egymást. Nagle olyan problémát próbált megoldani, melyben a küldő alkalmazás báj tonként adta át az adatokat a TCP-nek. A Clark által megoldott problémában a vevő alkalmazás kérte bájtonként az adatokat a TCP-től. Mindkét megoldás helyes és képe sek együtt működni. Az a cél, hogy a küldő ne adjon apró szegmenseket, és a vevő se kérjen kicsiket. A fogadó TCP-entitás tovább növelheti a teljesítőképességet, ha csak nagyobb egy ségekben frissíti az ablakot. A küldő TCP-entitáshoz hasonlóan a fogadónak is van le hetősége az adatok pufferelésére, így az alkalmazás egy REÁD hívását addig blokkol hatja, amíg egy nagyobb adatblokkot nem tud átadni. Ezzel csökken a TCP-hívások száma, ezzel együtt a túlterhelés (overhead) is. Ez természetesen a válaszidőt is meg növeli, de az állomány átviteléhez hasonló nem interaktív alkalmazások esetében a hatékonyság ellensúlyozhatja az egyes kérések megnövekedett válaszidejét. Egy másik feladat a vevő számára a rossz sorrendben érkező szegmensek kezelése.
A SZÁLLÍTÁSI RÉTEG
593
A vevőtől függ, hogy azokat megtartja vagy eldobja. Nyugta természetesen csak ak kor küldhető, ha a nyugtázott bájtig terjedő összes adat megérkezett. Ha a vevő a 0, 1, 2, 4, 5, 6 és 7 szegmenseket kapja meg, mindent nyugtázhat a 2. szegmens utolsó bájt jáig (azt is beleértve). Amikor a küldő időzítése lejár, ujraküldi a 3. szegmenst. Ha a vevő megtartotta a 4.-7. szegmenseket, a 3. szegmens vétele után a 7. szegmens utol só bájtjáig mindet nyugtázhatja.
6.5.9.
A TCP torlódáskezelése
Amikor bármely hálózatban a hosztok több forgalmat akarnak bonyolítani, mint amennyit a hálózat kezelni képes, torlódás keletkezik. Ez alól az Internet sem kivétel. Ebben a szakaszban olyan algoritmusokat fogunk megtárgyalni, amelyeket a torlódá sok kezelésére fejlesztettek ki az elmúlt negyed évszázad során. A hálózati réteg is megpróbálja kezelni a torlódásokat, de a munka oroszlánrészét mégis a TCP végzi, mivel a torlódások igazi ellenszere az, ha lecsökkentjük az adatsebességet. Elméletileg a torlódás egy fizikából kölcsönvett törvénnyel is kezelhető: ez a cso magmegmaradás törvénye. Az alapötlet az, hogy addig nem indítunk egy új csomagot a hálózatban, amíg egy régi el nem hagyja azt (tehát célba ér). A TCP az ablakméret dinamikus változtatásával próbálja ez a célt elérni. A torlódás kezelésének első lépése a torlódás detektálása. A régi időkben ez nehéz feladat volt. Ha egy időzítő lejárt egy elveszett csomag miatt, ennek oka egyaránt le hetett zaj az átviteli vonalon (1), vagy hogy egy túlterhelt router eldobta azt (2). Ezek között nehéz volt különbséget tenni. Mostanában az átviteli hibákból eredő csomagvesztés viszonylag ritka, mert a leg több nagytávolságú trönk üvegszálas (bár a vezeték nélküli átvitel külön történet). En nek következménye, hogy a legtöbb időtúllépés az Interneten torlódás eredménye. Az összes Interneten használt TCP-algoritmus feltételezi, hogy időtúllépés torlódás miatt következik be, és hasonlóan figyeli az időzítőket a baj előjelei után kutatva, mint ahogy a bányászok nézik a kanárimadaraikat. Mielőtt rátérnénk arra, hogy a TCP hogyan reagál a torlódásra, először beszéljünk arról, hogy mit is jelent elkerülni a torlódást. Amikor létrejön az összeköttetés, egy megfelelő ablakméretet kell választani. A vevő saját puffermérete alapján határozhatja meg az ablakméretet. Ha a küldő elfogadja ezt az ablakméretet, a vevőoldali puffer túlcsordulása nem okoz problémát, azonban a hálózatban fellépő torlódások így is bajt okoz hatnak. A 6.36. ábrán a probléma hidraulikai illusztrációját láthatjuk. A 6.36.(a) ábrán egy vastag cső torkollik kis kapacitású vevőbe. Ameddig a küldő nem ad több vizet, mint amennyi a vödörbe belefér, egy csöpp víz se megy kárba. A 6.36.(b) ábrán nem a vö dör mérete jelenti a szűk keresztmetszetet, hanem a hálózat belső szállítókapacitása. Ha túl gyorsan érkezik sok víz, emelkedni fog a csőben a vízszint és a folyadék egy része kárba fog veszni (ebben az esetben a tölcsér szélén csordul túl). Az Internet megoldása azon az észrevételen alapul, hogy két potenciális probléma létezik - a hálózat kapacitása és a vevő kapacitása -, melyeket külön kell kezelni. En nek érdekében minden adó két ablakot használ: az egyik ablakot a vevő szabályozza, a
594
?3i
SZÁMÍTÓGÉP-HÁLÓZATOK
Az átviteli sebesség szabályozása
Kis kapacitású vevő
6.36. ábra. (a) Gyors hálózat táplál kis kapacitású vevőt, (b) Lassú hálózat táplál nagy kapacitású vevőt másik pedig a torlódási ablak (congestion window). Mindkettő az adó által elküld hető bájtok számát mutatja. A ténylegesen továbbítható bájtok száma a két ablak érté kének minimuma, tehát az effektív ablak a küldő és a fogadó által jónak tartott érték minimumát tartalmazza. Ha a vevő 8 kilobájtot kér, de az adó tisztában van vele, hogy 4 kilobájtnál hosszabb löketek eltömítik a hálózatot, 4 kilobájt adatot fog küldeni. Másrészt, ha a vevő 8 kilobájtot kér, emellett a küldő tudja, hogy 32 kilobájtos löketek is zavartalanul átvihetők, a teljes 8 kilobájtos blokkot el fogja küldeni. Amikor egy összeköttetés létrejön, a küldő a torlódási ablak kezdőértékét az össze köttetésben használt legnagyobb szegmensméretre állítja be. Ezután elküld egy maxi mális szegmenst. Ha a szegmensre nyugta érkezik, mielőtt az időzítő lejárna, egy szegmensméretnyi bájttal növeli a torlódási ablak méretét, ami így a maximális szeg mensméret kétszerese lesz, és két szegmenst küld el. Amint mindkettőre megérkezett a nyugta, a torlódási ablakot ismét maximális szegmensmérettel növeli. Ha a torlódási ablak n szegmens méretű, és az n számú nyugta rendben megérkezik, a torlódási abla kot n szegmens méretének megfelelő számú bájttal növeli. Végül is minden sikeresen nyugtázott adatlöket hatására a torlódási ablak megduplázódik. A torlódási ablak, amíg időtúllépés nem lép föl vagy el nem éri a vevő ablakmé retét, exponenciálisan növekszik. Ha például 1024, 2048 és 4096 bájtos löketek könynyedén átvihetők, de egy 8192 bájtos löket továbbítása során időtúllépés következik be, a torlódási ablak méretét 4096 bájtra kell állítani, hogy a torlódást elkerülhessük.
595
A SZÁLLÍTÁSI RÉTEG
Amíg a torlódási ablak 4096 bájt hosszú marad, ennél hosszabb löketet nem továbbí tunk függetlenül attól, hogy a vevő" mekkorát engedélyez. Ezt a technikát lassú kez dést biztosító algoritmusnak (slow start) (Jacobson, 1988) nevezzük, amelyik azon ban egyáltalán nem lassú. Az algoritmus exponenciális működésű. Minden TCP-implementációnak támogatnia kell. Vegyük most szemügyre az Internet torlódásvédelmi algoritmusát. Ez a vételi és a torlódási ablakon kívül egy harmadik, torlódási küszöbnek (threshold) nevezett pa ramétert is használ, amelynek kezdőértéke 64 kilobájt. Amikor időtúllépés következik be, a torlódási küszöböt az aktuális torlódási ablak méretének felére állítjuk be, és a torlódási ablakot a maximális szegmensméretre állítjuk vissza. Ezek után meghatároz zuk a hálózat teljesítőképességét a lassú kezdet protokoll segítségével, amelyben itt egy apró módosítás történt. Az exponenciális növekedés véget ér, amikor az ablakmé ret eléri a torlódási küszöböt. Innentől kezdve minden sikeres adatátvitel lineárisan növeli a torlódási ablakot (löketenként egy maximális szegmens méretével) ahelyett, hogy szegmensenként növelné eggyel. Végül is ez az algoritmus úgy tippeli, hogy va lószínűleg jó közelítés lesz, ha megfelezi a torlódási ablakot, és onnan fokozatosan lépked felfelé. A torlódásvédelmi algoritmus működésének illusztrálására tekintsük a 6.37. ábrát. A maximális szegmensméret itt 1024 bájt. Kezdetben a torlódási ablak 64 kilobájt volt, de időtúllépés történt, ezért a torlódási küszöböt 32 kilobájtra állítottuk, a 0. átvi telhez használt torlódási ablakot pedig 1 kilobájtra. Ez innentől kezdve exponenciáli san növekszik, amíg el nem éri a torlódási küszöböt (32 kilobájt). Onnantól kezdve li neárisan nő tovább. Időtúllépés
10 12 14 Átvitel sorszáma
16
18
6.37. ábra. Példa az Internet torlódásvédelmi algoritmusának működésére
20
22
24
596
SZÁMÍTÓGÉP-HÁLÓZATOK
A 13. átvitel nem szerencsés (ezt sejthettük volna), időtúllépés történik. A küszöböt az aktuális ablakméret felére állítjuk (mostanra 40 kilobájt lett, így a fele 20 kilobájt), és a lassú kezdet protokollt újra elindítjuk. Amikor a 14. átviteltől kezdve sorra érkez nek a nyugták, az első négy mind megkétszerezi a torlódási ablakot, viszont azután a növekedés ismét lineáris lesz. Ha nem történik több időtúllépés, a torlódási ablak addig növekszik, amíg el nem éri a vevő ablakméretét. Ezen a ponton abbahagyja a növekedést, és állandó méretű marad, amíg időtúllépés nem következik be, vagy a vevő ablakmérete meg nem válto zik. Emellett azt az eseményt, amikor egy ICMP SOURCE QUENCH (lassítást kérő) cso mag fut be, amelyet a TCP megkap, pontosan úgy kezeli, mintha időtúllépés történt volna. Egy ettől eltérő (és ennél újabb keletű) megközelítést ír le a 3168-as RFC.
6.5.10.
A TCP időzítéskezelése
A TCP (elvileg) több időzítőt használ feladata elvégzéséhez. Ezek közül legfontosabb az ismétlési időzítő (retransmission timer). Egy szegmens elküldésekor az ismétlési időzítőt is elindítja. Ha a szegmensre az időzítő lejárása előtt nyugta érkezik, az időzí tő leáll. Ha viszont az időzítő még a nyugta beérkezése előtt lejár, a szegmenst újraküldi a TCP (és az időzítőt is újraindítja). Fölmerül a kérdés: milyen hosszú ideig fus son az időzítő? Ez egy sokkal nehezebb kérdés az Internet szállítási rétegében, mint amilyen a 3. fejezet általános protokolljaiban volt. A várható késleltetés ezekben a protokollokban nagymértékben kiszámítható volt (vagyis kicsi volt a szórása), így az időzítőt úgy le hetett beállítani, hogy kevéssel a nyugta várt megérkezése után járjon le. A késleltetés eloszlását a példa-protokollok esetére a 6,38.(a) ábrán tüntettük fel. Az adatkapcsolati 0,3r
0,3
Ti
-0) " • * •
c
'N
'N 03
19
fi
2 0,1
2 0,1
J_ 10 20 30 40 Körülfordulási idő (ms)
(a)
50
10 20 30 40 Körülfordulási idő (ms) (b)
6.38. ábra. (a) A nyugtabeérkezési idők sűrűségfüggvénye az adatkapcsolati rétegben, (b) A nyugtabeérkezési idők sűrűségfüggvénye a TCP-ben
Tp
597
A SZÁLLÍTÁSI RÉTEG
rétegben a nyugták (a torlódások hiánya miatt) ritkán késnek, ezért ha egy nyugta nem érkezik meg a várt időn belül, az általában azt jelenti, hogy vagy az adatkeret vagy a nyugta elveszett. A TCP ettől gyökeresen eltérő környezettel szembesül. A TCP-nyugták késleltetési idejének sűrűségfüggvénye sokkal inkább a 6.38.(b) ábrára hasonlít, mint a 6.38.(a) ábrára. A rendeltetési helyig terjedő körülfordulási időt nehéz megállapítani. Még ha ismert is, az időzítés időtartamáról is nehéz dönteni. Ha az időtartamot túl rövidre ál lítjuk be, mondjuk a 6.38.(b) ábra Tx értékére, felesleges újraküldések történnek, az Internetet haszontalan csomagok terhelik. Ha túl hosszúra állítjuk (T2), a teljesítőké pesség egy csomag elveszésekor a hosszú újraküldési késleltetés miatt csökken. Ezen kívül a nyugta érkezési idejének átlaga és szórásnégyzete is jelentősen változhat pár másodperc alatt, ha torlódás lép fel vagy szűnik meg. A megoldás az, ha erősen dinamikus algoritmust használunk, ami a hálózat teljesí tőképességének folyamatos mérése alapján állandóan újra beállítja az időintervallu mot. A TCP-ben általánosan használt algoritmus Jacobson (1988) nevéhez fűződik, és a következőképpen működik. A TCP minden összeköttetés részére fenntart egy RTTnek nevezett változót, ami a szóban forgó rendeltetési helyig terjedő körülfordulási idő legjobb jelenlegi becsült értéke. Egy szegmens elküldésekor egy időzítőt is elindít a TCP, hogy megmérje, mennyi idő alatt ér vissza a nyugta, és ha túl sokáig késik, újraküldhesse a csomagot. Ha a nyugta az időzítő lejárása előtt visszaér, a TCP megmé ri, hogy mennyi ideig tartott (M). Ezután az RTT = aRTT + (1 - a)M képlet szerint frissíti az RTT értékét, ahol a egy átlagoló tényező, azt határozza meg, hogy mekkora súlyt kapjon a régi érték. Tipikusan a = 7/8. Még RTTJó értékének tudatában sem triviális egy megfelelő ismétlési késleltetés kiválasztása. A TCP általában $RTT-t használ, viszont a trükk (3 megválasztásában van. Korai implementációkban [3 mindig 2 volt, de a tapasztalat azt mutatta, hogy a konstans érték rugalmatlanul viselkedik, nem tudja követni a változásokat, ha a szó rásnégyzet megnőtt. 1988-ban javasolta Jacobson, hogy legyen (3 nagyjából a nyugtabeérkezési idő sűrű ségfüggvényének szórásával arányos, tehát nagy szórásnégyzet nagy (3-t eredményez és fordítva. Lényegében a szórás átlagos szórással történő olcsó közelítését javasolta. Algo ritmusa egy másik csúszóátlagolással előállított változót is igényel, a Z)-vel jelölt szórást. Mindig, amikor egy nyugta beérkezik, a TCP kiszámolja a várt és megfigyelt értékek | RTT- M | különbségét. Ennek egy csúszóátlagolással számított értékét tárolja D: D = aD + (l-a)
\RTT-M\
ahol a lehet ugyanaz vagy más, mint amit RTT számításához használtunk. Habár D nem egyezik pontosan a szórással, mégis elég jó. Ráadásul Jacobson eljárást adott csupán egész összeadás, kivonás, eltolás felhasználásával történő kiszámítására. A legtöbb TCP-implementáció jelenleg ezt az algoritmust használja, az időzítés hosszát a következő összefüggés adja:
598
SZÁMÍTÓGÉP-HÁLÓZATOK
Időzítés = RTT + 4xD A 4-es szorzótényező választása valamennyire tetszőleges, de két jelentős előnnyel jár. Először is a néggyel történő szorzás megvalósítható egyetlen eltolással. Másod szor, lecsökkenti a fölösleges időtúllépések és újraküldések számát, mert a csomagok kevesebb mint egy százaléka érkezik a szórás négyszeresénél nagyobb késéssel. (Lé nyegében Jacobson először a 2 használatát javasolta, de későbbi tanulmányok szerint a 4 jobb teljesítőképességet eredményez.) Az RTT dinamikus becslésekor felmerül egy probléma: mi a teendő, ha egy szeg mens időzítése lejár, és újraküldik? Amikor beérkezik a nyugta, nem tudható, hogy az első átvitelre vonatkozik vagy az újabbra. Egy rossz tipp jelentősen megzavarhatja az RTT becslését. Phil Karn nehéz körülmények között fedezte fel ezt a problémát. 0 lel kes rádióamatőr, aki a TCP/IP-csomagok amatőr rádióval történő átvitelével foglalko zik, ami egy hírhedten megbízhatatlan médium (egy jó napon a csomagok fele is átjut hat). Javaslata egyszerű: ne frissítsük az RTT értékét újraküldött szegmensek esetén, hanem az időzítés hosszát minden kudarc esetén duplázzuk meg, amíg a szegmens vé gül át nem jut. Ezt a javítást Kam-féle algoritmusnak nevezik. A legtöbb TCPimplementációban alkalmazzák. A TCP nem csak az ismétlési időzítőt használja. Egy másik időzítő a folytatódó időzítő (persistence timer). Ezt az alábbi holtpont elkerülésére tervezték. A vevő küld egy nyugtát 0 ablakmérettel, amivel a küldőt várakozásra kéri. Később a vevő frissíti az ablakot, de a frissítést hordozó csomag elvész. Most mind a küldő és a foga dó arra vár, hogy a másik tegyen valamit. Amikor a folytatódó időzítő lejár, a küldő egy kérést küld a vevőnek, amire válaszul megkapja az ablakméretet. Ha ez még min dig 0, a folytatódó időzítőt újraindítja és az egész folyamat megismétlődik, különben, ha nagyobb 0-nál, megkezdheti az adatátvitelt. A harmadik időzítő, amelyet néhány implementáció használ, az életben tartó idő zítő (keepalive timer). Amikor egy összeköttetés már régóta tétlen, az életben tartó időzítő lejár, és ennek hatására a TCP ellenőrzi, hogy partnere még mindig működik-e. Ha a távoli entitás nem válaszol, az összeköttetés befejeződik. Ez a szolgáltatás ellent mondásos, mert növeli a túlterhelést, és egy átmeneti hálózatszakadás hatására befe jezhet egy amúgy még működő összeköttetést. Az utolsó időzítő, amit minden TCP-összeköttetésben alkalmaznak, az TIME WAIT állapotban az összeköttetés bontásakor használt időzítő. Ez a maximális csomag élettartam kétszereséig jár, hogy biztosítsa az összeköttetés lebontása után az össze köttetés összes korábban generált csomagjának kihalását.
6.5.11.
Vezeték nélküli TCP és UDP
Elméletileg a szállítási protokolloknak függetlennek kellene lenniük az alattuk fekvő hálózati réteg technológiájától. Lényegében a TCP-nek nem kellene azzal törődnie, hogy az IP fényvezető szálon vagy rádión keresztül működik-e. A gyakorlatban vi szont számít, mert a legtöbb TCP-implementációt gondosan optimalizálták olyan fel tételezéseket használva, amelyek érvényesek vezetékes hálózatokra, de vezeték nél-
599
A SZÁLLÍTÁSI RÉTEG
kuli hálózatokon kudarcot vallanak. A vezeték nélküli átvitel tulajdonságainak fi gyelmen kívül hagyása logikailag helyes TCP-implementációt eredményezhet, viszont a teljesítőképessége szörnyen kicsi lesz. A legfőbb problémát a torlódásvédelmi algoritmus jelenti. Manapság szinte minden TCP-implementáció azt feltételezi, hogy az időtúllépéseket torlódások okozzák, nem a csomagok elvesztése. Ebből következően, amikor az időzítő lejár, a TCP lassít és ki sebb sebességgel ad (lásd Jacobson-féle lassú kezdet algoritmus). Ezen megközelítés mögött meghúzódó gondolat a hálózat terhelésének csökkentése, és így a torlódás csökkentése. Sajnos a vezeték nélküli átviteli vonalak erősen megbízhatatlanok. Folyton csoma gokat veszítenek. Az elveszített csomagok kezelésének helyes megközelítése az, hogy újraküldjük azokat, mégpedig olyan gyorsan, ahogy csak lehet. A lassítás viszont csak ront a helyzeten. Ha mondjuk az összes csomag 20%-a elvész, és az adó 100 cso mag/másodperc sebességgel forgalmaz, az átbocsátóképesség 80 csomag/s. Ha a kül dő lelassít 50 csomag/s sebességre, az átbocsátóképesség 40 csomag/másodperc érték re esik vissza. Lényegében, ha egy csomag a vezetékes hálózaton elveszik, a küldőnek lassítania kell. Ha a csomag vezeték nélküli hálózaton vész el, az adónak intenzívebben kellene adnia. Ha a küldő nem tudja, hogy milyen hálózattal van dolga, nehéz meghozni a he lyes döntést. Gyakran az adó és a vevő közötti útvonal inhomogén. Lehet, hogy az első 1000 km vezetékes hálózat fölött fut, viszont az utolsó 1 km vezeték nélküli. Most még nehe zebb időtúllépés esetén helyesen dönteni, mert számít, hogy hol lépett föl a probléma. Bakne és Badrinath javaslata (1995) a közvetett TCP használata. Osszuk a TCP-összeköttetést két külön összeköttetésre, mint azt a 6.39. ábrán láthatjuk. Az első összeköttetés a küldőtől a bázisállomásig tart, a második a bázisállomástól a vevőig. A bázisállomás egyszerűen átmásolja a csomagokat az összeköttetések között mindkét irányba. Ez a módszer azzal az előnnyel jár, hogy így mindkét összeköttetés homogén. Az első összeköttetésen bekövetkező időtúllépések lelassítják a küldőt, ugyanakkor a má sodik összeköttetésen fellépők felgyorsítják. Más paramétereket is külön lehet beállí tani a két összeköttetésen. A módszer hátránya, hogy felrúgja a TCP-szemantikát. Mi vel az összeköttetés mindkét része teljes TCP-összeköttetés, a bázisállomás a szokásos módon nyugtáz minden TCP-szegmenst. Ezért viszont az, ha a küldőhöz beérkezik egy nyugta, még nem jelenti azt, hogy a vevő megkapta a szegmenst, csak annyit je lent, hogy a bázisállomáshoz eljutott. Küldő
1.TCP
6.39. ábra. Egy TCP-összeköttetés két külön összeköttetésre bontása
600
SZÁMÍTÓGÉP-HÁLÓZATOK
Egy másfajta megoldás, ami Balakrishnan és munkatársai (1995) nevéhez fűződik, nem töri meg a TCP-szemantikát. Működésének alapja több kisebb változtatás a bá zisállomás hálózati rétegének kódjában. A módosítások közül az egyik egy fürkésző ügynök beépítése, amely figyeli és gyűjti a mobil állomás felé tartó TCPszegmenseket és a visszaérkező nyugtákat. Amikor a fürkésző ügynök észrevesz egy mobil állomás felé tartó TCP-szegmenst, viszont a (viszonylag rövid) időzítése alatt nem érkezik onnan nyugta, egyszerűen újraküldi a szegmenst anélkül, hogy ezt közöl né a forrással. Szintén újraküld, ha a mobil állomásról kettőzött nyugták érkeznek, ami egyértelműen annak a jele, hogy a mobil hoszt valamit összekevert. A kettőzött nyugtákat eldobja, nehogy a forrás torlódás jeleként félreértelmezze őket. Ennek az átlátszóságnak az a hátránya, hogy amennyiben a vezeték nélküli vonal nagyon veszteséges, a forrás időzítése lejárhat, miközben nyugtára várakozik, és elin díthatja a torlódásvédelmi algoritmust. A közvetett TCP esetében a torlódásvédelmi algoritmus soha nem indul el, kivéve, ha ténylegesen torlódás van a hálózat vezetékes részében. Balakrishnan publikációja a mobil hosztnál elveszett szegmensek problémájára is tartalmaz megoldást. Amikor a bázisállomás szünetet fedez fel a beérkező sorszámok ban, egy TCP-opció felhasználásával szelektív ismétlést kér a hiányzó bájtokra. Ezzel a két módosítással a vezeték nélküli vonal mindkét irányban megbízhatóbbá tehető anélkül, hogy a forrás tudna róla, vagy a TCP szemantikája megváltozna. Bár az UDP-nek nincsenek olyan problémái, mint a TCP-nek, a vezeték nélküli kommunikáció ott is támaszt nehézségeket. A fő probléma az, hogy az UDP-t haszná ló programok nagyfokú megbízhatóságra számítanak. Tudják, hogy nem kapnak sem milyen garanciát, de így is szinte teljesen tökéletes szolgálatot várnak. A vezeték nél küli környezet messze lesz a tökéletességtől. Azoknak a programoknak, amelyek csak elfogadható költséggel képesek kezelni az elveszett UDP-üzeneteket, a teljesítőképes sége katasztrofális lesz, ha hirtelen egy olyan környezetből, ahol az üzenetek nagyon ritkán tűnnek el, olyanba kerülnek, ahol az üzenetek minduntalan elvesznek. A vezeték nélküli kommunikáció a teljesítőképességen túl további területekre is hatással van. Hogyan tud például egy mozgó hoszt megtalálni egy olyan helyi nyom tatót, amelyhez kapcsolódhat, ahelyett hogy a megszokott, saját nyomtatóját használ ná? Ehhez lazán kapcsolódik az, hogy hogyan lehet egy weboldalt még akkor is eljut tatni egy helyi cellához, ha a cella neve ismeretlen. Mindemellett a weboldalak terve zői általában azt tételezik fel, hogy nagy sávszélesség áll rendelkezésre. Ha minden oldal tartalmaz egy nagy ábrát, az nemkívánatos eredményekre vezethet. Előfordulhat, hogy az ábrát 10 másodpercig tart átvinni egy lassú, vezeték nélküli összeköttetésen, minden egyes alkalommal, amikor az oldalt lekérik. Ez rettenetesen felbosszantja a felhasználókat. Ahogyan a vezeték nélküli hálózatok egyre jobban elterjednek, a TCP vezeték nél küli alkalmazása egyre sürgetőbb problémát jelent. (Bálákat és mások, 2000; Ghani és Dixit, 1999; Huston, 2001; valamint Xylomenos és mások, 2001) számol be az ezen a területen végzett kutatásokról.
A SZÁLLÍTÁSI RÉTEG
6.5.12.
601
Tranzakciós TCP
Ebben a fejezetben korábban már megvizsgáltuk a távoli eljáráshívást, amely az egyik lehetséges megoldás a kliens-szerver-rendszerek megvalósítására. Az UDP is alkal mas lehet erre, amennyiben mind a kérés, mind a válasz elég kicsi ahhoz, hogy bele férjen egy-egy csomagba, valamint a kért művelet biztonságosan megismételhető (idempotens). Ha azonban ezek a feltételek nem teljesülnek, akkor az UDP használata kevésbé vonzó. Ha például a válasz meglehetősen nagy méretű lehet, akkor darabjait sorszámozni kell, és ki kell találni egy megoldást az elveszett darabok újraküldésére. Gyakorlatilag arra lenne szükség, hogy újra feltaláljuk a TCP-t. Nyilvánvaló, hogy ez nem jó megoldás, de az sem, ha magát a TCP-t használjuk erre a célra. A legfőbb kérdés a hatékonyság. Az RPC TCP feletti megvalósításában elküldött szegmensek szokásos sorozata a 6.40.(a) ábrán látható. A legjobb esetben is kilenc szegmensre van szükség. A kilenc szegmens a következő: 1. A kliens egy SYN szegmenst küld, hogy felépítse az összeköttetést. 2. A szerver egy ACK szegmenst küld, hogy nyugtázza a SYN szegmenst. 3. A kliens befejezi a háromutas kézfogást. 4. A kliens elküldi magát a kérést. 5. A kliens egy FIN szegmenssel jelzi, hogy kész van az adással. 6. A szerver nyugtázza a kérést és a FIN szegmenst. 7. A szerver visszaküldi a választ a kliensnek. 8. A szerver egy FIN szegmenssel jelzi, hogy kész van az adással. 9. A kliens nyugtázza a szervertől kapott [választ és a] FIN szegmenst. Fontos kiemelni, hogy ez a legjobb eset. A legrosszabb esetben a kliens kérését és a FIN szegmenst a szerver külön nyugtázza, és a kliens is külön-külön nyugtázza a szerver válaszát és a FIN szegmenst. Hamar felmerül az a kérdés, hogy lehetne-e valahogy egyesíteni az UDP-re épülő RPC hatékonyságát (mindössze két üzenet) a TCP megbízhatóságával. A válasz az, hogy egy kísérleti TCP-változattal ezt majdnem el lehet érni. Ezt a T/TCP (Transactional TCP - tranzakciós TCP) nevű protokollt az 1379-es és az 1644-es RFC írja le. Az elsődleges ötlet ebben a protokollban az, hogy az összeköttetések felépítéséhez szokásosan használt üzenetsorozatot kicsit megváltoztatva már a kapcsolatépítés köz ben elkezdődhet maga az adatátvitel is. A T/TCP-protokollt a 6.40.(b) ábrán mutatjuk be. Először a kliens egy olyan csomagot küld, amely a SYN bitet, magát a kérést és a
602
SZÁMÍTÓGÉP-HÁLÓZATOK
Szerver
Szerver SYNS YN, ACK(SYN)-
Idő * 5
-ACK(SYN) - Kérés -R
!
s, FIN-
. S YN,ACK(FIN), válasz, FINIdő
-ACK(FIN) -
^__ACK(kérés + FIN) •
ACK(FIN) — _
(a)
(b)
6.40. ábra. (a) Az RPC megvalósítása szokványos TCP-vel. (bj T/TCP-t használó RPC-megoldás FIN-t tartalmazza. Ezzel gyakorlatilag azt mondja, hogy „Létre szeretnék hozni egy kapcsolatot, itt vannak az adatok és vége". Amikor a szerver megkapja a kérést, megkeresi vagy kiszámítja a választ és eldön ti, hogy hogyan válaszoljon. Ha a válasz belefér egy szegmensbe, akkor a 6.40.(b) áb rán látható választ adja. Ez azt jelenti, hogy „Nyugtázom a F/iV-edet, itt a válasz és vége." A kliens ezután nyugtázza a szerver FIN szegmensét, és a protokoll három üzenet után véget ér. Ha azonban az eredmény hosszabb egy szegmensnél, akkor a szerver megteheti, hogy nem állítja be a FIN bitet. Ebben az esetben több szegmenst is elküldhet, mielőtt lezárja ezt az irányt. Talán érdemes megjegyezni, hogy a T/TCP nem az egyetlen olyan protokoll, ame lyet a TCP javítására javasoltak. Egy másik javaslat az SCTP (Stream Control Transmission Protocol - folyam vezérlő átviteli protokoll), amely többek között megőrzi az üzenethatárokat, valamint többféle kézbesítési módot (pl. nem sorrendhe lyes kézbesítés), többszörös címzést (vagyis másodlagos célállomásokat hiba esetére multihoming) és szelektív nyugtázást kínál (Stewart és Metz, 2001). Sajnos minden alkalommal, amikor valaki azt javasolja, hogy változtassunk valami olyanon, ami már régóta elég jól működik, nagy vita alakul ki a „felhasználók több lehetőséget akarnak" és a „ha nem romlott el, ne kezdjük megjavítani" táborok között.
6.6.
Teljesítőképesség
A teljesítőképességgel kapcsolatos kérdések nagyon fontosak a számítógép-hálózatok esetében. Amikor számítógépek százai vagy ezrei vannak összekapcsolva, mindenna posak az előre nem látható következményekkel járó bonyolult kölcsönhatások. Gyakran
A SZÁLLÍTÁSI RÉTEG
603
ez az összetettség gyenge teljesítőképességhez vezet, aminek senki sem tudja az okát. A következő alfejezetben sok hálózati teljesítőképességgel kapcsolatos kérdést meg vizsgálunk, hogy láthassuk a fennálló problémákat, és hogy mit lehet tenni ellenük. Sajnos a hálózat teljesítőképességének megértése inkább művészet, mint tudo mány. E mögött kevés gyakorlatban is valóban alkalmazható elmélet van. A legtöbb, amit tehetünk, hogy tapasztalatból nyert ökölszabályokat és életből vett példákat mu tatunk. Szándékosan halasztottuk ezt a témát a TCP szállítási rétegbeli megtárgyalása utánra, mert a TCP sok helyen jól használható példa lesz. Nem a szállítási réteg az egyetlen hely, ahol teljesítőképességgel kapcsolatos kér dések felmerülnek. Már láthattunk néhányat a hálózati rétegben az előző fejezetben. A hálózati réteg mégis afelé halad, hogy leginkább forgalomirányítással és torlódásvéde lemmel foglalkozzon. Az általánosabb, rendszerorientált feladatok egyre inkább a szállítással kerülnek kapcsolatba, tehát ez a fejezet megfelelő hely ezek tanulmányo zására. A következő öt részben a hálózat teljesítőképességét öt oldalról vizsgáljuk meg: 1. A teljesítőképesség problémái. 2. A hálózat teljesítőképességének mérése. 3. Rendszertervezés a teljesítőképesség növelésére. 4. Gyors TPDU-feldolgozás. 5. A jövő nagy teljesítményű hálózati protokolljai. Mellesleg nevet kell találnunk a szállítási entitások által váltott egységek számára. A TCP-szegmens elnevezése könnyen összezavarhat, ebben a környezetben a TCP-világon kívül sehol nem használják. A megfelelő ATM-kifejezések: CS-PDU, SARPDU és CPCS-PDU ATM-specifikusak. A csomag egyértelműen a hálózati rétegre utal, az üzenet az alkalmazási réteghez tartozik. Rendes kifejezés híján a szállítási en titások által váltott egységeket továbbra is TPDU-nak nevezzük. Ha egyszerre utalunk TPDU-ra és csomagra, a csomagot gyűjtőfogalomként fogjuk használni, mint pl. a kö vetkező mondatban: „A processzornak elég gyorsnak kell lenni ahhoz, hogy a beérke ző csomagokat valós időben feldolgozhassa." Ezzel egyaránt utalunk a hálózati réteg beli csomagra és a benne elhelyezkedő TPDU-ra is.
6.6.1.
A számítógép-hálózatok teljesítőképesség-problémái
Néhány teljesítőképességgel kapcsolatos problémát, mint pl. a torlódást, átmeneti erő forrás-túlterhelés okoz. Ha hirtelen nagyobb forgalom alakul ki egy routernél, mint amekkorát az kezelni képes, torlódás alakul ki és a teljesítőképesség romlik. A torló dást részletesen tanulmányoztuk az előző fejezetben. A teljesítőképesség akkor is csökken, ha strukturális erőforrás-kiegyensúlyozatlan ság áll fönn. Például, ha egy gigabites távközlési vonal csatlakozik egy kiskapacitású
604
SZÁMÍTÓGÉP-HÁLÓZATOK
PC-hez, a gyenge processzor képtelen lesz elég gyorsan feldolgozni a beérkező' cso magokat, tehát egy részük elvész. Ezeket a csomagokat később újraküldik, ami növeli a késleltetést, pazarolja a sávszélességet, általában véve rontja a teljesítőképességet. A túlterhelést egyidejű események is kiválthatják. Például, ha egy TPDU rossz pa ramétert (például rossz célportot) tartalmaz, sok esetben a figyelmes vevő egy hibajel zést küld vissza. Most képzeljük el, mi történne, ha a rossz TPDU üzenetszórással 10 000 géphez jutna el: mindegyik visszaküldhet egy hibajelzést. Az ebből kialakuló üzenetszórás vihar (broadcast storm) romba döntheti a hálózatot. Az UDP ebben a betegségben szenvedett, amíg a protokollt olyan értelemben meg nem változtatták, hogy a hosztok ne küldjenek hibajelzést üzenetszórásos címre érkezett TPDU-kra. Egy másik példa egyidejű események által okozott túlterhelésre, amely egy áram szünet után következhet be. Amikor helyreáll az energiaszolgáltatás, az összes gép egyszerre nekilát a ROM-ban tárolt bootprogram végrehajtásához. Egy tipikus újraindulási menetrend megkövetelheti, hogy a hoszt először egy (RARP) szervert keressen fel, hogy megtudja valódi azonosítóját, majd egy állományszolgáltatóról letöltse az operációs rendszerét. Ha gépek százai egyszerre tesznek így, a szerver valószínűleg összeomlik a terhelés alatt. Még ha egyidejű események nem is okoznak túlterhelést, és elegendő erőforrás áll rendelkezésre, a rendszer rossz beállítása is oka lehet a gyenge teljesítőképességnek. Például, ha egy gépnek bőven van szabad processzorkapacitása és memóriája, viszont kevés memóriát foglaltak le pufferterület céljára, ráfutások fognak előfordulni és TPDU-kat kell eldobnia. Hasonlóan, ha az ütemező nem ad elég nagy prioritást a be érkező TPDU-k feldolgozására, egy részük el fog veszni. Egy másik beállításokkal kapcsolatos kérdés az időzítések megfelelő értékének meghatározása. Amikor egy TPDU-t elküldenek, rendszerint elindítanak egy időzítőt, ami a TPDU elvesztését figyeli. A túl rövid időtartam fölösleges újraküldésekhez ve zet, terheli a vonalakat. Ha túl hosszúra állítják, egy TPDU elvesztését túlságosan hosszú késleltetések követik. További példák a beállítható paraméterekre: mennyi ideig kell adatra várni, hogy ráültetett nyugtát lehessen küldeni különálló nyugta he lyett, vagy mennyi az újraküldések száma, mielőtt a küldő az ismétlést föladná. A gigabites hálózatok új teljesítőképességgel kapcsolatos problémákat hoznak ma gukkal. Tekintsük például azt az esetet, amikor 64 kilobájt adatot küldünk San Diegóból Bostonba, és a vevőnek 64 kilobájtos puffere van. Tegyük fel, hogy a vonal sebes sége 1 Gb/s és az üvegszálban a fénysebességből adódó késleltetés egy irányban 20 ms. Kezdetben f = 0-ban a csővezeték üres, ezt láthatjuk a 6.41.(a) ábrán. Alig 500 usmal később a 6.41.(b) ábrának megfelelően az összes TPDU úton van az üvegszálon. Az első TPDU most valahol Brawley magasságában járhat, még mindig mélyen DélKaliforniában. A küldőnek azonban meg kell állnia, amíg ablakfrissítést nem kap. 20 ms elteltével az első TPDU eléri Bostont - mint azt a 6.41.(c) ábrán láthatjuk és a vevő nyugtázza. Végül 40 ms-mal a kezdés után az első nyugta visszaér a küldő höz, ami elküldheti a második löketet. Mivel az átviteli vonalat csak fél ezredmásod percig használták a 40-ből, a hatékonyság körülbelül 1,25 százalék. Ez a helyzet tipi kusan akkor fordul elő, ha régi protokollokat használnak gigabites vonalak fölött. Egy hasznos mennyiséget érdemes megjegyezni, ha számítógép-hálózatok teljesí tőképességének vizsgálatával foglalkozunk. Ez a sávszélesség-késleltetés szorzat
605
A SZÁLLÍTÁSI RÉTEG
(c)
(d)
6.41. ábra. Egy megabit továbbítása San Diegóból Bostonba, (a) t = 0-kor. (b) 500 /JS mú (c) 20 ms elteltével, (d) 40 ms múlva (bandwidth-delay product), ami úgy áll elő, hogy megszorozzuk a b/s-ban mért sáv szélességet a másodpercben mért körülfordulási idővel. A szorzat a küldőtől a vevőig és vissza terjedő csővezeték bitben mért kapacitása. Például a 6.41. ábrán a sávszélesség-késleltetés szorzat 40 millió bit. Másképpen szólva a küldőnek 40 millió bites löketet kellene folyamatosan teljes sebességgel ad nia, amíg az első nyugta vissza nem érkezik. Ennyi sok bit szükséges a csővezeték te letöltéséhez (mindkét irányban). Ezért jelent egy félmillió bites löket csak 1,25 szá zalékos hatékonyságot: a csővezeték kapacitásának csak 1,25 százaléka. Azt a tanulságot vonhatjuk le, hogy jó teljesítőképesség eléréséhez a vevő ablaká nak legalább akkorának kell lennie, mint a sávszélesség-késleltetés szorzat, lehetőleg még egy kicsit nagyobbnak, hiszen a vevő esetleg nem tud azonnal válaszolni. Egy kontinenseket összekötő gigabites vonalon minden összeköttetés számára legalább 5 megabájt szükséges. Ha a hatékonyság már egy megabit átvitelekor is szörnyű, képzeljük el, hogy mi lyen lesz, ha pár száz bájtot küldünk egy távoli eljáráshíváshoz. Hacsak nem találunk valami más feladatot a vonalnak, amíg a kliens válaszra vár, a gigabites vonal nem jobb egy megabites vonalnál, csak drágább. Egy másik teljesítőképességgel kapcsolatos probléma, a dzsitter (csomagok érkezé si idejének változása) időkritikus alkalmazásoknál - mint pl. hang- és videoátvitel lép föl. Nem elegendő, ha az átviteli idők átlaga kicsi, a szórásának is kicsinek kell lennie. Komoly tervezői erőfeszítést igényel rövid átlagos átviteli idő és kis szórás egyidejű megvalósítása.
606 6.6.2.
SZÁMÍTÓGÉP-HÁLÓZATOK
A hálózati teljesítőképesség mérése
Amikor egy hálózat gyengén teljesít, a felhasználók gyakran panaszkodnak a működ tetőknek, és fejlesztést követelnek. A teljesítőképesség javításához az operátoroknak először is meg kell állapítaniuk, hogy mi is történik pontosan. Hogy megtudják, mi történik valójában, méréseket kell végezniük. Ebben a részben a hálózatok teljesítőké pességének mérését fogjuk bemutatni. Az alábbiakban leírtak Mogul munkáján (1993) alapulnak. A hálózat teljesítőképességének javítása a következő lépések ismételt végrehajtásá val lehetséges: 1. Megmérjük a fontosabb hálózati paramétereket és teljesítőképességet. 2. Megpróbáljuk megérteni, hogy mi is történik. 3. Egy paramétert megváltoztatunk. Ezeket a lépéseket addig ismételjük, amíg a teljesítőképesség már elég jó nem lett, vagy már mindent kifacsartunk a hálózatból. A méréseket (mind fizikailag és a protokollkészlet minden szintjén) sokféleképpen és sok helyen lehet végezni. A legalapvetőbb mérés az időmérés, amikor valamilyen tevékenység kezdetén egy időzítőt indítunk, amellyel megmérjük, hogy az adott tevé kenység mennyi időt vesz igénybe. Például annak ismerete, hogy mennyi időt igényel egy TPDU nyugtázása, kulcsfontosságú. Más mérések számlálók felhasználásával le hetségesek, ezek valamely esemény gyakoriságát rögzítik (pl. elvesztett TPDU-k szá ma). Végül gyakran fontos tudni valaminek a mennyiségét, például egy bizonyos idő intervallum alatt feldolgozott bájtok számát. A hálózat teljesítőképességének és paramétereinek mérése számos potenciális buk tatót rejt. Az alábbiakban néhányat fölsorolunk. Minden hálózati teljesítőképesség mérésére tett szisztematikus próbálkozás során gondosan el kell kerülni ezeket.
Győződjünk meg, hogy elég nagy-e a minták száma! Ne egyetlen TPDU elküldésének idejét mérjük, hanem ismételjük meg a mérést mondjuk egymilliószor és vegyük az átlagot. Nagyszámú minta vizsgálata a mért átlag és szórás bizonytalanságát is csökkenti. Ezt a bizonytalanságot statisztikai képletekkel határozhatjuk meg.
Bizonyosodjunk meg arról* hogy reprezentatív mintákat használunk! Ideális esetben az egymillió mérést különböző napszakokban és a hét napjain meg kellene ismételni, hogy láthassuk a különböző rendszerterheléseknek a mért mennyi ségre gyakorolt hatását. A torlódáson végzett mérések például kevés haszonnal járnak,
A SZÁLLÍTÁSI RÉTEG
607
ha a mérés pillanatában nincs is torlódás. Néha az eredmények első ránézésre valószí nűtlennek tűnhetnek, mint pl. súlyos torlódás 10, 11, 1 és 2 órakor, viszont nincs tor lódás délben (amikor az összes felhasználó ebédelni ment).
Bánjunk óvatosan a durva felbontású órával! A számítógépes órák úgy működnek, hogy szabályos időközönként egy számláló érté két eggyel növelik. Például egy ezredmásodperces időzítő ezredmásodpercenként egyet ad a számláló értékéhez. Ilyen időzítő felhasználásával nem lehetetlen 1 ms-nál rövidebb eseményt mérni, csak gondosságot igényel. Hogy például egy TPDU elküldéséhez szükséges időt megmérjük, kétszer kell leol vasni a rendszerórát (mondjuk ezredmásodpercekben) amikor belépünk a szállítási ré teg kódjába, és amikor elhagyjuk azt. Ha a TPDU tényleges elküldési ideje 300 us, a két olvasás különbsége 0 vagy 1 lesz, ami egyaránt rossz. Ha azonban a teljes mérést egymilliószor megismételjük, és az összes mérési eredményt összeadjuk, majd egy millióval elosztjuk, az átlagos idő 1 us-nél is pontosabb lesz.
Győződjünk meg arról, hogy mérés közben nem következik be váratlan esemény! Ha azon a napon végzünk egy egyetemi rendszeren méréseket, amikor egy nagyobb laborfeladatot kell beadni, más eredményeket kaphatunk, mint ha a mérést a követ kező napon végeznénk. Hasonlóan, ha néhány kutató úgy döntött, hogy videokonfe renciát tart a mérés alatt levő hálózaton, hibás eredményeket kaphatunk. A legjobb tét len rendszeren végezni a méréseket, és a teljes terhelést saját kezűleg generálni, azon ban még ennek a megközelítésnek is akadnak buktatói. Amíg úgy gondolnánk, hogy senki sem fogja használni a hálózatot éjjel háromkor, éppen ekkor foghat hozzá egy önműködő másolatkészítő program az összes merevlemez videoszalagra másolásához. Továbbá erős forgalmat generálhatnak a csodálatos World Wide Web oldalainkat né zegető más időzónában élő felhasználók.
A gyorsítótár működése romba döntheti a mérést Az állományátvitel idejének mérése nyilvánvalóan a következő módon történhet: egy nagy állományt megnyitunk, az egészet beolvassuk, majd lezárjuk és megnézzük, hogy ez mennyi ideig tartott. Az egész mérést sokszor megismételjük, hogy egy jó átlagot kapjunk. Akkor van baj, ha a rendszer gyorsítótárba (cache) rakja az állo mányt, ezért csak az első mérés alatt folyik tényleges hálózati forgalom. A maradék csak a helyi gyorsítótár olvasása. Az ilyen mérésekből származó eredmények lényegében értéktelenek (hacsak nem a gyorsítótár teljesítőképességére vagyunk kíváncsiak). Gyakran megkerülhetjük a cache használatát, ha túlcsordulást okozunk benne. Pél dául, ha a gyorsítótár kapacitása 10 megabájt, egy ciklus minden menetben megnyit-
608
SZÁMÍTÓGÉP-HÁLÓZATOK
hat, beolvashat és lezárhat két 10 megabájtos állományt, hogy megpróbálja a cachetalálatok számát 0-ra szorítani. Még ekkor is érdemes vigyázni, hacsak teljesen biz tosan nem értjük a cache algoritmus működését. A pufferelésnek hasonló hatása lehet. Egy népszerű TCP/IP teljesítőképességet vizsgáló segédprogram arról volt ismert, hogy az UDP teljesítőképességére a fizikai vonal által megengedettnél jóval nagyobb értéket közölt. Hogy történhetett ez meg? Egy UDP-hívás normálisan akkor adja vissza a vezérlést, amikor az üzenetet elfogadta a mag, és beillesztette a továbbítási sorba. Ha elegendő pufferterület van, 1000 UDPhívás nem jár együtt az összes adat tényleges továbbításával. A legtöbb üzenet esetleg még mindig a magban várakozik, de a segédprogram úgy gondolja, hogy már mindet elküldték.
Értsük meg azt, amit mérünk! Amikor egy távoli állomány beolvasásának idejét mérjük, a mérések függnek a háló zattól, mindkét gép operációs rendszerétől, az esetünkben használt illesztőkártyáktól, azok meghajtóprogramjától és egyéb tényezőktől. Ha gondosan dolgozunk, végül megkapjuk az állományátvitel idejét a használt konfigurációra vonatkozóan. Ha a cé lunk ennek a bizonyos konfigurációnak a beállítása, ezek a mérések tökéletesek. Ha azonban három különböző rendszeren végzünk hasonló méréseket, hogy el dönthessük, melyik hálózati illesztőkártyát vásároljuk meg, az eredményeinket telje sen meghamisíthatja, ha az egyik meghajtóprogram csapnivaló, és a kártya teljesítőké pességének csak 10 százalékát használja.
Vigyázzunk az eredmények extrapolálásával! Tegyük fel, hogy valamit szimulált hálózati terhelésnél mérünk. A terhelés 0 (tétlen) értéktől 0,4-re (a kapacitás 40 százaléka) nő, ezt jelzik a 6.42. ábrán látható pontok és a rájuk fektetett folytonos vonal. Nehéz ilyenkor ellenállni a kísértésnek, hogy lineá-
/ 2 3 N
|g 2
0
0,1
0,2
0,3
0,4
0,5 0,6 Terhelés
6.42. ábra. A válaszidő a terhelés függvényében
0,7
0,8
0,9
1,0
A SZÁLLÍTÁSI RÉTEG
609
risan extrapoláljuk, amit a pontozott vonal mutat. A sorban állás behoz; azonban egy 1/(1 - p) tényezőt is, ahol a p a terhelés, ezért az igazi eredmény inkáb^ a s z a g g a t o t t görbére emlékeztet.
6.6.3.
Rendszertervezés a teljesítőképesség növelésére
Mérések végzése és javítgatás jelentős mértékben növelheti a teljesítők^pesse- t ^ nem helyettesítheti az elsődleges fontossággal bíró gondos tervezést. g„ gyengén megtervezett hálózatot csak úgy-ahogy lehet javítani, azon túl az alapo,^] k e l ] u - a kezdeni. Ebben a részben néhány ökölszabályt mutatunk be, melyek sok háló 2 a t o n v é „ z e t t munka tapasztalatain alapulnak. Ezek a szabályok rendszertervezéssel i s k a p c s o i a t o _ sak, nem csak hálózatok tervezésével, hiszen a szoftver és az operációs renc jszer sok szor fontosabb a routereknél és a csatlakozókártyáknál. Ezen ötletek nagy r g s z e hosszú évek óta a hálózattervezők közös ismerete, és szájhagyomány útján terjec} generációról generációra. Először Mogul (1993) rögzítette őket; a mi megközelítést^ nagv jából párhuzamos az övével. Egy másik idekapcsolódó forrás Metcalfe (1993) m u - v e
Első szabály: a processzor sebessége fontosabb a hálózat sebességénél A tapasztalat szerint szinte minden hálózatban az operációs rendszer é s a protokoll overheadje határozza meg a vonalon töltött idő döntő részét. Például, elméletileg egv Etherneten eltöltött legrövidebb RPC végrehajtási idő 102 us, ami megfedj e „ y m[n[. malis (64 bájtos) kérésnek és az azt követő minimális (szintén 64 bájtos) válasznak A gyakorlatban jelentős eredménynek számít, ha a szoftver overhead leszorításával az RCP-időre valamilyen ehhez közeli eredményt kapunk. Hasonlóan, az 1 Gb/s sebességű átvitel legnagyobb problémája a bit^ e jg g g V o r s átvitele felhasználói pufferből az üvegszálra, és a vevő processzorral oly an serjességű feldolgozást elérni, amilyen sebességgel a bitek beérkeznek. Röviden szq v a ^ a m e g _ duplázzuk a processzor sebességét, gyakran közel kétszeresére növelhe^^ a z ^Q. csátóképességet is. A hálózat kapacitásának megduplázása gyakran telj e s e n hatásta lan, mivel a szűk keresztmetszetet általában a hosztok jelentik.
Második szabály: a szoftver overhead csökkentéséhez csökkentsük a csomagok számát Egy TPDU feldől gozása TPDU-nként (például a fejrész értelmezése) és bájtonként (például ellenőrző összeg számítása) bizonyos mennyiségű overheaddel j ^ j j a e „ y . millió bájtot küldünk el, a bájtonkénti overhead a TPDU méretétől függetlenül ugyanaz 128 bájtos TPDU használata azonban 32-szer akkora TPDU-nkénti over}jea[jet j e i e n t mint 4 kilobájtos TPDU-k esetében. Ez a fajta overhead gyorsan jelentőssé válik
SZÁMÍTÓGÉP-HÁLÓZATOK
610
A TPDU overheaden kívül az alsó rétegek overheadjét is számításba kell venni. Minden beérkező csomag megszakítást okoz. Egy modern RISC processzor esetén minden megszakítás kiüríti a processzor csővezetékét, megváltoztatja a gyorsítótárat, környezetváltást eredményez, és rengeteg CPU-regiszter elmentését kényszeríti ki. Egy n-szeres csökkenés az elküldött TPDU-k számában a megszakítás- és a csomagoverheadet is n-ed részére csökkenti. Ez a megjegyzés azt sugallja, hogy a távoli oldal megszakításainak csökkentése érde kében érdemes sok adatot átvitel előtt összegyűjteni. A Nagle-féle algoritmus és Clark megoldása a buta ablak jelenségre ennek precíz elvégzésére tett próbálkozások.
Harmadik szabály: minimalizáljuk a környezetváltások számát A környezetváltások (például rendszer módból felhasználói módba) életveszélyesek. Ugyanazok a rossz tulajdonságaik vannak, mint a megszakításoknak, melyek közül a legrosszabb az, hogy kezdetben sokáig nem lesz cache-találat. A környezetváltásokat megritkíthatjuk, ha az adatokat küldő könyvtári eljárást addig kényszerítjük adatok gyűjtésére, amíg jelentős mennyiségű össze nem gyúlt. Hasonlóképpen a vevőoldalon a kicsi beérkező TPDU-kat össze kell gyűjteni, és egyenként történő átadás helyett egy mozdulattal átdobni a felhasználónak, hogy minimalizálni lehessen a környezetváltások számát. A legjobb esetben egy beérkező csomag környezet váltást okoz az aktuális felhasz nálóról a magra, majd a vevő folyamatra, hogy az beolvashassa a frissen érkezett adato kat. Sajnos, sok operációs rendszer esetében még további környezetváltások is történ nek. Például, ha a hálózatmenedzser speciális folyamatként felhasználói módban fut, egy csomag érkezése valószínűleg egy környezetváltást okoz az aktuális felhasználóról a magra, majd onnan a hálózatmenedzserre, ezt követően egy újabb környezetváltás tör ténik vissza a magra, és végül egy utolsó vissza a vevő folyamatra. Ezt a sorozatot lát hatjuk a 6.43. ábrán. Ezek a csomag érkezésekor bekövetkező környezetváltások na gyon sok processzoridőt elpazarolnak és lerontják a hálózat teljesítőképességét. A csomag érkezésekor futó felhasználói folyamat
Hálózatmenedzser
Vevő folyamat
Felhasználói mód
Rendszermód (Kernel)
6.43. ábra. Egy csomag felhasználói módban futó hálózatmenedzserrel történő feldolgozásához szükséges négy környezetváltás
A SZÁLLÍTÁSI RÉTEG
611
Negyedik szabály: minimalizáljuk a másolást Többszörös másolatok készítése még a környezetváltásoknál is rosszabb. Nem szokat lan, hogy egy beérkező csomagot háromszor vagy négyszer átmásolnak a benne fog lalt TPDU kézbesítése előtt. Miután a csomag megérkezett a hálózat illesztő egy spe ciális, a kártyán található pufferébe, tipikusan egy magpufferbe másolják. Onnan a há lózati réteg pufferébe kerül, majd a szállítási réteg pufferébe, végül a fogadó alkalma zási folyamathoz. Egy okos operációs rendszer egyszerre egyetlen szót másol, de nem szokatlan, hogy öt utasítás szükséges szavanként (egy olvasás, egy tárolás, egy indexregiszter növelése, a másolás végének ellenőrzése és egy feltételes elágazás). Egy 50 MlPS-es gépen minden csomagról három másolat készítése (32 bites) szavanként öt utasítás végrehajtása esetén beérkező bájtonként 75 ns-ot vesz igénybe. Egy ilyen gép ezért legfeljebb 107 Mb/s sebességgel érkező adatokat tud fogadni. Ha a fejrészfeldolgozás ból, megszakításkezelésből és környezetváltásból eredő overheaddel is számolunk, jó, ha 50 Mb/s elérhető, és az adatok tényleges feldolgozását még mindig nem vettük szá mításba. Világos, hogy egy 1 Gb/s sebességű vonal kezelésére nem is gondolhatunk. Feltehetően egy 500 Mb/s-os vonalat sem tudnánk teljes sebességgel kezelni. A fenti számításban azt tettük fel, hogy egy 500 MIPS számítási sebességű gép 500 mil lió utasítást hajt végre másodpercenként. A gyakorlatban a gépek csak akkor tudnak ekkora sebességgel működni, amikor nem hivatkoznak a memóriára. A memóriamű veletek gyakran egy nagyságrenddel lassabbak (pl. 20 nsec/művelet). Ha a feldolgozás során az összes utasítás 20%-a hivatkozik a memóriára (vagyis okoz laphibát a gyorsítótárban), ami valószínű, amikor a bejövő csomagokkal kell valamit kezdeni, akkor az egyes utasításokhoz átlagosan szükséges idő 5,6 nsec (0,8 x 2 nsec + 0,2 x 20 nsec). Ha bájtonként négy műveletet kell elvégezni, akkor 22,4 nsec/bájt (vagy 2,8 nsec/bit) feldolgozási időre van szükség, ami körülbelül 357 Mb/s-ot jelent. Ha 50%os rendszertöbblettel számolunk, akkor csak 178 Mb/s marad. Fontos megjegyezni, hogy a hardveres támogatás itt nem jöhet szóba. A probléma az, hogy az operációs rendszer túl sokat másol.
Ötödik szabály: nagyobb sávszélességet lehet vásárolni, de kisebb késleltetést nem A következő három szabály inkább a kommunikációra vonatkozik, mint a protokollal végzett feldolgozásra. Az első szabály szerint, ha nagyobb sávszélességet akarunk, azt csak megvásárolni lehet. Egy második üvegszálat helyezve az első mellé a sávszéles ség a kétszeresére nő, viszont a késleltetés semennyit sem csökken. A késleltetés csökkentése a protokoll szoftver, az operációs rendszer vagy a hálózati interfész fej lesztését teszi szükségessé. Még ha ezek közül mindegyiket el is végeztük, a késlelte tés nem fog csökkenni, ha a szűk keresztmetszet az átvitel ideje.
612
SZÁMÍTÓGÉP-HÁLÓZATOK
Hatodik szabály: jobb elkerülni a torlódást, mint utána talpra állni A régi mondás, miszerint egy szem megelőzés felér egy véka gyógyítással, biztosan igaz a hálózati torlódásokra is. Amikor torlódás van egy hálózaton, csomagok vesznek el, sávszélesség megy kárba, haszontalan késleltetések lépnek föl és így tovább. Utána a talpra állás időt és türelmet igényel. Legjobb, ha nem hagyjuk, hogy előforduljon. A torlódás elkerülése olyan, mint a védőoltás: egy kicsit fáj, amikor beadják, de megelőz valamit, ami sokkal fájdalmasabb lenne egy későbbi időpontban.
Hetedik szabály: az időtúllépések elkerülése Az időzítők szükségesek a hálózatban, de módjával kell használni őket, és az időtúllé pések számát minimalizálni kell. Amikor egy időzítő lejár, általában egy tevékenység megismétlődik. Ha tényleg szükség van az ismétlésre, ám legyen, de a fölösleges is métlés pazarlás. A pluszmunka elkerülésének az a módja, hogy az időzítőket gondosan, inkább egy kicsit hosszabbra állítjuk. Egy időzítő, ami hosszabb ideig működik, egy kis extra kés leltetést csinál az összeköttetésen abban a (valószínűtlen) esetben, ha egy TPDU el vész. Az az időzítő, ami lejár, amikor még nem kellene, értékes processzoridőt hasz nál, sávszélességet pazarol, és fölösleges többletterhelést okoz akár több tucat routernek is.
6.6.4.
Gyors TPDU-feldolgozás
A fenti történet tanulsága az, hogy a gyors hálózat legfőbb kerékkötője a protokoll szoftver. Ebben a részben ennek a szoftvernek felgyorsítására adunk néhány módszert. További információ (Clark és mások, 1989; valamint Chase és mások, 2001) művei ben található. A TPDU feldolgozási overheadjének két összetevője van: a TPDU-nkénti és a bájtonkénti overhead. Mindkettő ellen harcolni kell. A gyors TPDU-feldolgozás kulcsa az, hogy válasszuk külön a normális esetet (egyirányú adatátvitel), és kezeljük külön leges módon. Bár egy sor speciális TPDU szükséges ahhoz, hogy eljussunk az ESTABLISHED állapotba, ha már ott vagyunk, a TPDU-k feldolgozása nyilvánvaló, amíg egyik nem kezdeményezi az összeköttetés bontását. Kezdjük a tanulmányozást az ESTABLISHED állapotban levő küldő oldalán, ami kor van átküldésre váró adat. Az egyértelműség kedvéért feltételezzük, hogy a szállí tási entitás a mag része, bár ugyanezeket az ötleteket lehet alkalmazni akkor is, ha ez egy felhasználói folyamat, vagy ha egy könyvtári eljárás a küldő folyamatban. A 6.44. ábrán a küldő folyamat SEND mag hívást ad ki. Az első dolog, amit a szállítási entitás csinál, hogy megvizsgálja, normális esetről van-e szó: az állapot ESTABLISHED, egyik oldal se próbálja bontani az összeköttetést, egy szabályos (azaz nem sávon kívü li) teljes TPDU-t küldenek, és a vevőnél elég nagy ablak áll rendelkezésre. Ha minden
613
A SZÁLLÍTÁSI RETEQ
d>
Küldő folyamat Trap a magba TPDU küldéséhez
~> Hálózat Fogadó folyamat -^ A fogadó folyamatnak átadott TPDU
'©
6.44. ábra. A küldőtől a vevőig vezető gyors utat vastag vonal jelzi. Ezen út feldolgozási lépéseit besatíroztuk feltétel teljesül, nincs szükség további tesztekre, és a gyors utat lehet követni a szállí tási entitáson belül. A legtöbb rendszerben a TPDU-k többnyire ezt az útvonalat kö vetik. Normális esetben az egymást követő adat TPDU-k fejrészei majdnem azonosak. Hogy ezt a tényt kiaknázhassa, a szállítási entitás egy fejrész prototípust tárol. A gyors út elején, amilyen gyorsan lehet, ezt szavanként egy átmeneti pufferbe másolja. Azo kat a mezőket, amelyek TPDU-ról TPDU-ra változnak, a pufferben fölülírja. Ezek a mezők - mint például a sorszám - gyakran egyszerűen származtathatók az állapotvál tozókból. Ezután átad a hálózati rétegnek két, a teljes hálózati fejrészre, illetve a fel használói adatra mutató mutatót. Itt ugyanezt a stratégiát lehet követni (a 6.44. ábrán nem tüntettük fel). Végül a hálózati réteg továbbításra átadja a csomagot az adatkap csolati rétegnek. Hogy lássuk az elmélet gyakorlati működését, vegyük például a TCP/IP-t. A 6.45.(a) ábrán a TCP-fejrészt láthatjuk. Azokat a mezőket, amelyek az egyirányú folyamban egymás után következő TPDU-kban azonosak, besatíroztuk. A küldő szállítási entitás tennivalója mindössze öt szót átmásolni a fejrész prototípusból a kimeneti pufferbe, kitölteni a sorszám mezőt (egy szó bemásolása a memóriából), kiszámítani az ellenőr ző összeget és növelni a memóriában a sorszám értékét. Ez után átadhatja a fejrészt és
614
SZÁMÍTÓGÉP-HÁLÓZATOK
Célport
Forrásport Sorszám
Ellenőrző összeg
Teljes hossz
TOS
Datagrameltolás
Azonosítás
Nyugta Len Unused
VER. IHL
TTL
A fejrész Protokoll 3llenőrző összege
Ablakméret
Forráscím
Sürgősségi mutató
Célcím
(a)
(b)
6.45. ábra. (a) TCP-fejrész. (b) IP-fejrész. Mindkét esetben a satírozott mezőket változtatás nélkül veszik a prototípusból az adatot egy speciális IP-eljárásnak, hogy továbbítson egy szabályos, teljes méretű TPDU-t. Az IP ezt követően átmásolja a saját ötszavas fejrész prototípusát [lásd 6.45.(b) ábra] a pufferbe, kitölti az azonosítás mezőt és kiszámítja a saját ellenőrző összegét. A csomag most átvitelre kész. Most vizsgáljuk meg a vevő oldalon alkalmazott, a 6.44. ábrán is látható gyors fel dolgozást. Az első lépés az, hogy meg kell találni a bejövő TPDU-hoz tartozó össze köttetés-rekordot. A TCP esetében az összeköttetés-rekordokat egy hash-táblában tá rolhatjuk, a két IP-cím valamely egyszerű függvénye szerint rendezve. Miután a TCPentitás az összeköttetés-rekordot megtalálta, entitás mindkét címet és mindkét portot összehasonlítja a rekordban találhatókkal, így ellenőrizve azt, hogy a talált rekord a megfelelő rekord-e. Egy, a legutóbb használt rekordra mutató mutató gyakran tovább gyorsíthatja az összeköttetés-rekord előkeresesét, ha először mindig azzal próbálkozik a TCP. Clark és munkatársai (1989) kipróbálták ezt, és 90 százalékosnál magasabb találati arányt tapasztaltak. Más keresési heurisztikákkal McKenney és Dove (1992) foglalkozott. Ez után az entitás megvizsgálja, hogy normális TPDU érkezett-e: az állapot ESTABLÍSHED, egyik oldal se próbálja bontani az összeköttetést, a TPDU maximális méretű, nincs speciális jelzése, és a sorszám egyezik a várt értékkel. Ezek a vizsgála tok csak néhány utasítást igényelnek. Ha minden feltétel teljesül, a vezérlés egy speci ális gyors út eljárásra kerül a TCP-n belül. A gyors út rutin frissíti az összeköttetés-rekordot és átmásolja az adatot a felhasz nálóhoz. Másolás közben ellenőrző összeget is számít, így elkerülhető egy további adatfeldolgozási menet. Ha az ellenőrző összeg rendben van, frissíti az összeköttetés rekordot, és visszaküld egy nyugtát. Azt az általános módszert, aminek során először gyors ellenőrzéssel megbizonyosodik arról, hogy a várt fejrész érkezett-e meg, majd az eset kezelését speciális eljárás végzi, ezt fejrész-előrejelzésnek (header prediction) nevezik. Sok TCP-implementáció alkalmazza. Ha ezt, és az itt leírt többi optimalizációt együtt használják, elérhető, hogy a TCP a helyi memória-memória másolás sebességének 90 százalékával fusson, feltéve, hogy maga a hálózat elég gyors. Két másik terület, ahol jelentős teljesítőképesség-javulás érhető el, a pufferkezelés és az időzítőkezelés. A pufferkezelés trükkje - mint fent említettük - a fölösleges má solás elkerülése. Az időzítőkezelés fontos, mert szinte egyetlen elindított időzítő sem
615
A SZÁLLÍTÁSI RÉTEG
jár le. A TPDU-k elvesztését figyelik, de a legtöbb TPDU és visszaküldött nyugta rendben megérkezik. Ezért fontos arra optimalizálni az időzítők kezelését, hogy ritkán járnak le. Általánosan használt módszer az, hogy lejárat szerint sorba rendezve láncolt lis tában tároljuk az időzítő eseményeket. A lista feje egy számlálót tartalmaz, ami azt mutatja, hogy mostantól számítva hány óraiités múlva jár le a hozzá rendelt időzítő. Az egymásra következő bejegyzések azt tartalmazzák, hogy az egymás után követke ző időzítők az előzőtől számítva mikor járnak le. így ha az időzítők 3, 10 és 12 ütem múlva járnak le, a három számláló értéke rendre 3, 7 és 2 lesz. Minden óraütéskor a lista fejének számlálóját csökkenti az entitás. Amikor eléri a 0-t, a hozzá tartozó eseményt feldolgozza és a következő elem lesz a lista feje. Ennek nem kell módosítani a számlálóját. Ebben a megoldásban időzítők beszúrása és törlése drága művelet, aminek végrehajtási ideje arányos a lista hosszával. Egy hatékonyabb megközelítés alkalmazható, ha a maximális időtartam értéke kor látos és előre ismert. Itt egy időkeréknek (timing wheel) nevezett tömböt haszná lunk, amit a 6.46. ábrán láthatunk. Minden sor egy óraütésnek felel meg. Az ábrán a 7 = 4 időpillanatot ábrázoltuk. Az időzítők mostantól számítva 3, 10 és 12 óraütés múlva járnak le. Ha hirtelen egy hét ütem múlva lejáró új időzítőt kell beszúrni, csak egy új bejegyzést nyitunk a 11. sorban. Hasonlóan, ha a 7 + 10 időpontra beállított időzítőt törölni akarjuk, a 14. sorban kezdődő listát kell megkeresni és a kívánt elemét törölni. Vegyük észre, hogy a 6.46. ábrán látható tömb nem tud T+ 15-nél későbbre állított időzítőket kezelni. Amikor üt az óra, az aktuális idő mutatóját egy sorral előremozdítjuk (cirkulári san). Ha a bejegyzés, amire most mutat, nullától különbözik, az ott lévő összes időzí tőt feldolgozzuk. Az alapötlet több variációját tárgyalja Varghese és Lauck (1987). So 0 1 2 3
/\ 5 6 7 8 9 10 11 12 13 14 15
Mutató a 7+12 időpontban lejáró időzítőkre 0 0 0 0 0 0
Aktuális időpont, T
*
Mutató a í + 3 időpontban lejáró időzítőkre 0 0 0 0 0 0 0
6.46. ábra. Az időkerék
*
Mutató a T+10 időpontban lejáró időzítőkre
616 6.6.5.
SZÁMÍTÓGÉP-HÁLÓZATOK
Gigabites hálózatok protokolljai
Az 1990-es évek elején kezdtek megjelenni a gigabites hálózatok. Az emberek először ezeken is a régi protokollokat próbálták alkalmazni, de hamarosan számtalan megol dandó problémával kellett szembesülniük. Ebben a szakaszban az imént említett problémák közül fogunk néhányat megtárgyalni, valamint azokat az irányokat, ame lyekbe az egyre gyorsuló hálózatokra kifejlesztett új protokollok mutatnak. Az első probléma az, hogy sok protokoll 32 bites sorszámozást alkalmaz. Amikor az Internet elkezdődött, a routerek közötti vonalak a legtöbb helyen 56 kb/s-os bérelt vonalak voltak, ezért egy teljes sebességgel adó hosztnak is több, mint egy hét kellett ahhoz, hogy egyszer felhasználja az összes sorszámot. A TCP tervezői számára a 232 még elég jól közelítette a végtelent, mivel kevés esély volt arra, hogy a régi csomagok még a hálózatban legyenek egy héttel a feladásuk után. Egy 10 Mb/s-os Ethernet ese tében a körülfordulási idő 57 perc, ami sokkal rövidebb, de még kezelhető idő. Ha azonban egy 1 Gb/s-os Ethernet teljes sebességgel zúdít adatokat az internetre, akkor a körülfordulási idő körülbelül 34 másodperc, ami már jóval az interneten megenge dett 120 másodperces legnagyobb csomagélettartam alatt van. A 2 32 hirtelen már nem közelíti jól a végtelent, mivel a küldő akkor is kimerítheti a sorszámokat, amikor a ré gi csomagok még a hálózaton tartózkodnak. Az 1323-as RFC egy lehetséges kiutat mutat ebből a helyzetből. Az a probléma, hogy sok protokolltervező anélkül, hogy rögzítette volna, egyszerű en azt feltételezte, hogy az összes sorszám elhasználásához szükséges idő jóval na gyobb a maximális csomagélettartamnál. Következésképpen még gondolni sem kellett arra a problémára, hogy régi kettőzések még mindig létezhetnek, amikor a sorszámok körbefordulnak. Gigabites sebességnél ezek a ki nem mondott feltételezések kudarcot vallanak. A második probléma az, hogy a kommunikációs sebességek sokkal gyorsabban nö vekedtek, mint a számítási sebességek. A hetvenes években az ARPANET 56 kb/s se bességgel működött, a számítógépek körülbelül 1 MlPS-esek voltak. A csomagok hossza 1008 bit volt, ezért az ARPANET körülbelül 56 csomagot tudott másodper cenként továbbítani. A csomagonként rendelkezésre álló 18 ezredmásodperc alatt egy hoszt 18 000 utasítást szánhatott egy csomag feldolgozására. Természetesen ez föl emésztette volna az egész processzort, ezért, ha 9000 utasítást szentelt egy csomagra, még mindig megmaradt a fél processzor a valódi munka elvégzésére. Hasonlítsuk össze ezeket a számokat a modern 100 MlPS-es számítógépek eseté vel, melyek 4 kilobájtos csomagokat cserélnek egy gigabites vonalon. Másodpercen ként 30 000 csomag folyhat be, ezért egy csomag feldolgozását meg kell oldani 15 (ÍS alatt, ha a fél processzort alkalmazások számára akarjuk fenntartani. 15 us alatt egy 100 MlPS-es számítógép 1500 utasítást tud végrehajtani, ez csak egyhatoda annak, ami egy ARPANET hoszt rendelkezésére állt. Ezenkívül a modern RISC-utasítások kevesebb dolgot végeznek el, mint amennyit régi CISC-utasítások tettek meg, ezért a probléma még rosszabb, mint amilyennek tűnik. A tanulság az, hogy kevesebb idő van protokollfeldolgozásra, mint annak idején volt, ezért a protokolloknak egyszerűb beknek kell lenniük. A harmadik probléma az, hogy az n visszalépéses protokoll gyengén teljesít nagy
617
A SZÁLLÍTÁSI RÉTEG
lományá [vitelhez szükséges dő
sávszélesség-késleltetés szorzattal rendelkező vonalakon. Vegyünk például egy 1 Gb/s sebességgel működő 4000 km-es vonalat. A körülfordulási idő 40 ms, mialatt a küldő 5 megabájtot tud elküldeni. Ha a vevő hibát detektál, ez 40 ms alatt jut az adó tudomá sára. N visszalépéses protokoll alkalmazása esetén a küldőnek nemcsak a rosszat, ha nem esetleg az egész utána következő 5 megabájtnyi csomagot is újra kell küldenie. Világos, hogy ez óriási erőforrás-pazarlás. A negyedik probléma a gigabites vonalakkal kapcsolatban, hogy ezek alapvetően mások, mint a megabites vonalak: a hosszú vonalak inkább késleltetés-, semmint sáv szélesség-korlátosak. A 6.47. ábrán egy 1 megabites állomány 4000 km-es távolságra történő elküldésének időszükségletei láthatók különböző átviteli sebességek esetén. 1 Mb/s sebességig az átviteli időben az adási sebessége meghatározó. 1 Gb/s-nél a 40 ms-es körülfordulási idő dominál az 1 ms fölött, ami a bitek üvegszálba töltéséhez szükséges. A sávszélesség további növelése aligha járhat eredménnyel. A 6.47. ábrán a hálózati protokollok szerencsétlen hatásait láthatjuk. Ezek szerint az RPC-hez hasonló megáll-és-vár protokollok teljesítőképességének veleszületett fel ső korlátja van. Ezt a határt a fénysebesség szabja meg. Az optika területén semmi lyen mértékű technikai előrelépés nem fejlesztheti a használt anyagokat (habár új fizi kai törvények segítenének). Egy ötödik említésre érdemes probléma a többitől eltérően nem technológiai, vagy protokollokkal kapcsolatos, hanem az új alkalmazások következménye. Ma már ter mészetes, hogy sok gigabites alkalmazásnak, mint például a multimédia, a csomagbe érkezési idők szórása éppolyan fontos, mint maga a késleltetések átlaga. A kicsi, de egyenletes kézbesítés gyakran előnyösebb egy nagy sebességű, viszont ugráló átvitelnél. Foglalkozzunk most a problémák helyett kezelési módjaikkal. Először néhány álta lános megjegyzést teszünk, aztán áttekintjük a protokollmechanizmusokat, csomagok felépítését és a protokoll-szoftvert.
•ro N
<
1000s 100 s 10s 1s 100 ms 10 ms 1 ms 10 3
104
105
10 6
10 7
10 8
10 9
10 10
10 11
10 12
Átviteli sebesség (b/s) 6.47. ábra. / megabites állomány átviteléhez és nyugtázásához szükséges idő 4000 km hosszú vonalon
618
SZÁMÍTÓGÉP-HÁLÓZATOK
Az alapvető elv, amit minden gigabites hálózat tervezőjének kívülről kellene tudni: Tervezni sebesség-, és nem sávszélesség-optimumra kell. A régi protokollokat gyakran úgy tervezték, hogy minimalizálják a vonalon tartóz kodó bitek számát, gyakran oly módon, hogy kicsi mezőket használtak és bájtokba vagy szavakba zsúfolták azokat. Manapság elegendő sávszélesség áll a rendelkezé sünkre. A protokollok feldolgozási lépéseivel van baj, ezért a protokollokat úgy kell tervezni, hogy minimalizáljuk a feldolgozást. Tisztán látszik, hogy az IPv6 tervezői megértették ezt a követelményt. Eró's lehet a kísértés a gyors működést a hálózati illesztők hardver megvalósításá val elérni. Ez a stratégia azért nehézkes, mert hacsak nem végtelenül egyszerű a proto koll, a hardver csak egy bedugható kártyát jelent egy másik processzorral és a saját programjával. Hogy elkerüljék a hálózati koprocesszor használatát, amely annyira drága, mint a központi processzor, gyakran egy lassúbb IC-t használnak. Ennek a ter vezésnek az a következménye, hogy a fő (gyors) processzor idejének nagy részét tét lenül tölti arra várakozva, hogy a második (lassú) processzor elvégezze a kritikus munkát. Tévhit, hogy a fő processzornak várakozás közben van más tennivalója. Ezenkívül, amikor két általános célú processzor kommunikál egymással, versenyhely zetek alakulhatnak ki, ezért kifinomult protokollok szükségesek helyes szinkronizálá sukhoz. Általában az a legjobb megközelítés, hogy egyszerű protokollokat készítünk, és a központi processzorral végeztetjük el a munkát. Vegyük most szemügyre a visszacsatolás kérdését a nagy sebességű protokollok ban. A (viszonylag) hosszú késleltetésű hurok miatt a visszacsatolást el kell kerülni: túl sokáig tart, amíg a vevő jelez a küldőnek. A visszacsatolás egy példája az átviteli sebesség szabályozása csúszóablakos protokollal. Hogy elkerüljük a (hosszú) késlelte téseket, melyek abból adódnak, hogy a vevő ablakfrissítéseket küld az adónak, előnyösebb egy sebesség alapú protokoll használata. Ebben a megoldásban a küldő min dent elküldhet, amit akar, feltéve, hogy nem küld gyorsabban egy bizonyos sebesség nél, amiben a küldő és a vevő előre megállapodott. Egy másik példa a visszacsatolásra a Jacobson-féle lassú kezdet algoritmus. Ez az algoritmus több próbálkozást végez, hogy megállapítsa a hálózat terhelhetőségét. Nagy sebességű hálózatoknál a hálózat viselkedését mérő fél tucat ilyen kis próba ha talmas méretű sávszélességet pazarol. Egy hatékonyabb módszer az, ha az összekötte tés felépítésekor a küldő, a vevő és a hálózat mind lefoglalja a szükséges erőforrá sokat. Az erőforrások előre történő lefoglalása azzal az előnnyel is jár, hogy így egy szerűbben csökkenthető a dzsitter. Röviden szólva nagyobb sebességek világában a tervezés feltartóztathatatlanul az összeköttetés alapú működés felé tart, vagy legalább is valami ahhoz nagyon közelihez. A csomagok felépítése fontos tényező a gigabites hálózatokban. A fejrésznek a fel dolgozási idő csökkentése érdekében olyan kevés bitet kell tartalmaznia, amennyit csak lehetséges. Ezeknek a mezőknek ugyanakkor elég nagynak kell lenni ahhoz, hogy feladatukat elláthassák, és a feldolgozás megkönnyítéséhez szóhatáron kell kez dődniük. Ebben a környezetben „elég nagy" azt jelenti, hogy olyan problémák ne for dulhassanak elő, mint a sorszámok körbefordulása, miközben a régi csomagok még
A SZÁLLÍTÁSI RÉTEG
619
élnek, vagy a vevők nem tudnak elég nagy ablakméretet bejelenteni, mert túl kicsi az ablak mező" és így tovább. A fejrészt és az adatot külön ellenőrző összeggel kell ellátni két okból kifolyólag is. Egyrészt, hogy lehetőség legyen csak a fejrészre számítani, és az adatot kihagyni. Másrészt azért, hogy ellenőrizni lehessen a fejrészt az adat felhasználói területre való másolása előtt. Célszerű akkor végezni az adatok ellenőrző összegének számítását, miközben az adat másolása folyik a felhasználói területre, mert ha a fejrész sérült, a másolás rossz folyamathoz történhet. Hogy elkerülhető legyen a rossz helyre történő másolás, de az ellenőrző összeget másolás közben lehessen kiszámítani, lényeges, hogy a két ellenőrző összeg egymástól független legyen. A maximális elküldhető adatmennyiségnek nagynak kell lenni, hogy hatékony le hessen a működés még hosszú késleltetések esetén is. Emellett minél nagyobb az adat blokk, a sávszélességből annál kisebb részt foglalnak el a fejrészek. Egy másik értékes képesség egy normális mennyiségű adat elküldésének lehetősé ge az összeköttetés-kérés során. Ily módon egy oda-vissza út ideje megtakarítható. Végül érdemes néhány szót szólni a protokoll-szoftverról. A kulcsgondolat az, hogy a sikeres esetre kell összpontosítani. Sok régi protokoll azt próbálta kihangsú lyozni, hogy mi a teendő hiba esetén (pl. amikor elvész egy csomag). Gyorsan futó protokollok készítéséhez a tervezőnek meg kell próbálnia csökkenteni a feldolgozási időt abban az esetben, ha minden jól megy. Másodlagos fontosságú a hibás esetre mi nimalizálni. Egy másik szoftverkérdés a másolás idejének minimalizálása. Mint korábban lát tuk, az adatok másolása sokszor az overhead fő forrása. Ideális esetben a hardvernek minden beérkező csomagot folytonos adatblokk formájában kellene a memóriába töl tenie. A szoftvernek ezután egyetlen blokkmozgatással a pufferbe kellene ezt másol nia. A gyorsítótár működésétől függően még egy másoló ciklus használata is kerülen dő lehet. Másképpen, 1024 szó másolásának leggyorsabb módja 1024 MOVE utasítás (vagy 1024 betöltés-tárolás pár) kiadása. A másolórutin annyira kritikus, hogy gondo san meg kell formálni assembly nyelven, hacsak nem lehet a fordítót ügyesen arra kényszeríteni, hogy pontosan az optimális kódot generálja.
6.7.
Összefoglalás
A szállítási réteg megértése kulcsfontosságú a rétegekbe szervezett protokollok meg értéséhez. Számos különböző szolgálatot kínál, amelyek közül a legfontosabb a vég pontok közötti, összeköttetés alapú, megbízható bájtfolyam a küldőtől a vevőig. Eh hez egy olyan szolgálatprimitív-készlet segítségével lehet hozzáférni, amely az össze köttetések kiépítését, használatát és lebontását teszi lehetővé. Egy gyakran alkalmazott szállítási réteg interfész a Berkeley-csatlkozók (sockets) által nyújtott interfész. A szállítási protokollóknak képesnek kell lenniük arra, hogy összeköttetés-kezelést végezzenek egy megbízhatatlan hálózaton. Az összeköttetések kiépítését az olyan késleltetett és megkettőzött csomagok léte nehezíti, amelyek teljesen váratlan pilla natokban újra felbukkanhatnak. Emiatt háromutas kézfogásra van szükség az össze-
620
SZÁMÍTÓGÉP-HÁLÓZATOK
köttetések felépítéséhez. Az összeköttetések bontása könnyebb, mint a kiépítésük, de a két hadsereg problémája miatt még így is távol áll a triviálistól. A szállítási rétegnek még akkor is sok dolga van, ha a hálózati réteg teljesen meg bízható. Kezelnie kell az összes szolgálatprimitívet, az összeköttetéseket és az idó'zítőket, valamint adási jogokat kell adnia és felhasználnia. Az Internet két fó' szállítási protokollt használ, az UDP-t és a TCP-t. Az UDP egy összeköttetés nélküli protokoll, amely lényegében egy olyan héj az IP-csomagok kö rül, amely lehetővé teszi több folyamat multiplexelését és a demultiplexelését egyet len IP-címen. Az UDP kliens-szerver párbeszédek lefolytatására alkalmas, például RPC használatával. Felhasználható még az RTP-hez hasonló valós idejű protokollok építőelemeként. Az Internet fő szállítási protokollja a TCP, amely megbízható, kétirá nyú bájtfolyamot biztosít. Minden szegmensben 20 bájtos fejrészt alkalmaz. A szeg menseket az Interneten belüli routerek feldarabolhatják, ezért fontos, hogy a hosztok képeseknek legyenek a darabok újbóli összerakására. Nagymennyiségű munka fekszik a TCP teljesítmény-optimalizálásában, amely Nagle, Clark, Jacobson, Karn és mások algoritmusait használja fel. A vezeték nélküli összeköttetések használata számos mó don bonyolítja a TCP alkalmazását. A tranzakciós TCP a TCP olyan kiterjesztése, amely kevesebb csomaggal valósítja meg a kliens-szerver párbeszédeket. A hálózat hatékonysága általában főként a protokoll és a TPDU-feldolgozás kom munikációs költségén múlik, és a sebesség növelésével a helyzet egyre romlik. A protokollokat arra kell tervezni, hogy a lehető legkevesebbre csökkentsék a felhasz nált TPDU-k, a szükséges környezetváltások és az egyes TPDU-k lemásolásainak számát. A gigabites hálózatok egyszerű protokollokat igényelnek.
Feladatok 1. A 6.2. ábrán látható szállítási primitív példánkban a listen blokkoló hívás. Feltét lenül szükséges ez? Ha nem, magyarázzuk el, hogyan lehetne egy nem blokkoló primitívet használni! Milyen előnnyel rendelkezne ez a szövegben leírtakkal szemben? 2. A 6.4. ábra modelljében azt feltételeztük, hogy a hálózati réteg csomagokat ve szíthet, ezért, azokat egyenként nyugtázni kell. Tegyük fel, hogy a hálózati réteg 100 százalékosan megbízható, és sohasem veszít csomagokat. Milyen változtatá sokat kell - ha egyáltalán szükséges - a 6.4. ábrán végezni? 3. A 6.6. ábra mindkét részén szerepel egy megjegyzés arról, hogy a SERVER_PORT értékének a kliensnél és a szervernél meg kell egyeznie. Miért olyan fontos ez? 4. Tegyük fel, hogy a kezdeti sorszámok előállítására az óravezérelt módszert használ juk 15 bites számlálóval mint órával. Az óra 100 ezredmásodpercenként üt egyet, a maximális csomagélettartam 60 s. Milyen gyakran történik újraszinkronizáció
A SZÁLLÍTÁSI RÉTEG
621
(a) a legrosszabb esetben? (b) amikor az adatok percenként 240 sorszámot használnak el? 5. Miért kell a T maximális csomagélettartamnak olyan nagynak lenni, hogy bizto sítsa nemcsak a csomagok, hanem a nyugtáik kihalását is? 6. Képzeljük el, hogy összeköttetések létesítéséhez nem háromutas, hanem kétutas kézfogás protokollt használunk. Másképpen fogalmazva a harmadik üzenet nem szükséges. Kialakulhatnak ekkor holtpontok? Adjunk egy példát, vagy bizonyít suk be, hogy nem alakulhatnak ki! 7. Képzeljünk el egy általánosított n-hadsereg problémát, amelyben bármely két hadsereg megegyezése elegendő a győzelemhez. Van olyan protokoll, ami győze lemre viszi a kékeket? 8. Tekintsük a hosztösszeomlásokból történő talpra állás problémáját (lásd a 6.18. ábrát). Ha az írás és nyugta küldése (vagy a fordított sorozat) közötti időinterval lum viszonylag kicsivé tehető, mi a küldő illetve a vevő legjobb stratégiája a pro tokoll hibázási esélyének minimalizálására? 9. Lehetséges-e holtpont kialakulása a szövegben leírt szállítási entitás esetében (6.20. ábra)? 10. A 6.20. ábra szállítási entitásának írója kíváncsiságból úgy döntött, hogy számlá lókat épít a sleep eljárásba, hogy statisztikát készítsen a conn tömbről. Többek között a hét állapot közül az egyes állapotokban levő összeköttetések ni(i= 1,..., 7) számát is vizsgálta. Miután az adatok analizálására írt egy komoly FORTRAN programot, programozónk észrevette, hogy a 2« ; = MAXjÖSSZ egyenlőség min dig teljesülni látszik. Van ezenkívül valamilyen állandó mennyiség, ami csak ezt a hét változót tartalmazza? 11. Mi történik, ha a 6.20. ábra szállítási entitásának felhasználója 0 hosszúságú üze netet küld? Vitassuk meg a válasz jelentőségét! 12. Minden, a 6.20. ábra szállítási entitásában potenciálisan előforduló eseményre dönt sük el, hogy legális-e vagy sem, amikor a felhasználó sending állapotban alszik! 13. Soroljuk fel a hitel alapú protokoll előnyeit és hátrányait a csúszóablakos proto kollal szemben! 14. Miért van az UDP? Nem volna elegendő a felhasználói folyamatokra hagyni a nyers IP-csomagok küldését? 15. Tekintsünk egy egyszerű, UDP-re épülő alkalmazási szintű protokollt, amely azt
622
SZÁMÍTÓGÉP-HÁLÓZATOK
teszi lehetővé a kliensnek, hogy egy jól ismert címen elérhető távoli szerverről állományokat kérjen le. A kliens először egy olyan kérést küld, amelyben az ál lomány neve szerepel. A szerver adatcsomagok olyan sorozatával válaszol, amely a kért állomány darabjait tartalmazza. A kliens és a szerver a megáll-és-vár proto koll használatával biztosítja a megbízhatóságot és a sorrendhelyes kézbesítést. Lát hiányosságot ebben a protokollban a nyilvánvalóan rossz teljesítőképességen kí vül? Gondolja jól végig, hogy mi történik, ha valamelyik folyamat lefagy! 16. Egy kliens 128 bájtos kérést küld egy tőle 100 km távolságra levő szervernek egy 1 gigabites üvegszálon. Mi a vonal hatékonysága a távoli eljáráshívás alatt? 17. Tekintsük ismét az előző feladatban vázolt helyzetet! Számítsuk ki az elképzelhe tő legkisebb válaszidőt az adott 1 Gb/s sebességű' vonalra és egy IMb/s sebessé gűre is! Milyen következtetést vonhatunk le? 18. Az üzenetek kézbesítésénél mind az UDP, mind a TCP portszámokat használ a célentitás azonosítására. Adjon két okot arra, hogy miért találtak ki ezekhez a protokollokhoz egy új absztrakt azonosítót (a portszámokat) ahelyett, hogy a fo lyamat-azonosítókat használták volna, amelyek már akkor is léteztek, amikor eze ket a protokollokat tervezték. 19. Mi a TCP-ben lehetséges legkisebb MTU méret, ha beleszámítjuk a TCP és az IP keretezését, de nem számítjuk bele az adatkapcsolati réteg overheadjét? 20. A datagramok feldarabolását és összeállítását az IP végzi a TCP számára láthatat lan módon. Ez azt is jelenti, hogy a TCP-nek nem kell aggódnia az adatok rossz sorrendben érkezése miatt? 21. Az RTP-t általában CD-minőségű hang továbbítására használják, amely két 16 bites mintavételt jelent 44 100-szor másodpercenként (egy-egy minta mindkét hang csatornáról). Hány csomagot kell másodpercenként elküldenie ebben az esetben az RTP-nek? 22. Lehetséges lenne az RTP kódját az UDP kódjához hasonlóan beleépíteni az ope rációs rendszer magjába? Válaszát indokolja! 23. Az 1. hoszt egyik folyamata a p portra kapcsolódik, a 2. hoszt egyik folyamata pedig a q portra. Lehetséges, hogy a p és q portok között egyszerre kettő vagy több TCP-összeköttetés legyen nyitva? 24. A 6.29. ábrán azt láthattuk, hogy a 32 bites Nyugta mezőn kívül egy ACK bit is ta lálható a negyedik szóban. Tényleges többletet jelent ez? Miért, illetve miért nem? 25. Egy TCP-szegmens maximális adatmezeje 65 495 bájt. Miért választottak ilyen furcsa számot?
A SZÁLLÍTÁSI RÉTEG
623
26. Mutassunk két példát arra, hogy hogyan kerülhet a 6.33. ábra véges automatája a SYNRCVD állapotba! 27. Mondjunk példát a Nagle-féle algoritmus egy lehetséges hátrányára, amikor egy erős torlódástól szenvedő hálózatban ezt az algoritmust használjuk. 28. Tekintsük a lassú kezdet protokoll hatását egy 10 ms körülfordulási idővel rendel kező torlódásmentes vonalon. A vevő ablaka 24 kilobájt, a maximális szegmens méret 2 kilobájt. Mennyi idő telik el, amíg az első tele ablak elküldhető? 29. Tegyük fel, hogy a TCP torlódási ablak 18 kilobájtra van állítva, és időtúllépés következik be. Mekkora lesz az ablak, ha a következő négy löket mind sikeresen átjut? A maximális szegmensméretet vegyük 1 kilobájtnak. 30. Ha a TCP RTT körülfordulási ideje jelenleg 30 ms, és a következő nyugták rendre 26, 32 és 24 ezredmásodperc elteltével érkeznek, mi az új becsült RTT érték? a-t vegyük 0,9-nek! 31. Egy TCP-t futtató gép 65 535 bájtos ablakokat küld egy 1 Gb/s sebességű csator nán, melynek egyik irányban mért késleltetése 10 ms. Legfeljebb mekkora átbo csátóképesség érhető el? Mekkora a vonal hatékonysága? 32. Mi a legnagyobb vonali sebesség, amellyel egy hoszt anélkül adhat 1500 bájtos TCP-adatmezőket, hogy a sorszámokat kimerítené? Vegyük figyelembe a TCP, az IP és az Ethernet többletbitjeit is! Feltehetjük, hogy az egyes csomagok legfeljebb 120 másodpercig létezhetnek, valamint hogy az Ethernet-kereteket a hoszt folya matosan tudja adni. 33. Mekkora a maximális adatsebesség összeköttetésenként abban a hálózatban, melyben a maximális TPDU méret 128 bájt, a maximális TPDU élettartam 30 másodperc és 8 bites sorszámokat használnak? 34. Tegyük fel, hogy egy TPDU vételéhez szükséges időt mérjük. Amikor megszakí tás történik, leolvassuk az ezredmásodperces felbontású rendszerórát. Amikor a TPDU-t teljesen feldolgoztuk, ismét leolvassuk az óra állását. 270 000 alkalom mal kapunk 0 ms eredményt és 730 000 alkalommal 1 ms-et. Mennyi ideig tart egy TPDU vétele? 35. Egy processzor 100 MIPS sebességgel hajtja végre az utasításokat. Egyszerre 64 bit adatot tud átmásolni, és minden szó másolása hat utasításba kerül. Ha egy be érkező csomagot kétszer kell átmásolni, képes ez a rendszer 1 Gb/s sebességű vo nal kezelésére? Az egyszerűség kedvéért tegyük fel, hogy minden utasítás, bele értve a memóriaírásokat és -olvasásokat, a teljes 100 MlPS-es sebességgel hajtha tó végre.
624
SZÁMÍTÓGÉP-HÁLÓZATOK
36. Hogy elkerüljük azt a problémát, hogy a sorszámok körbefordulásakor még élnek régi csomagok, 64 bites sorszámokat használhatunk. Elméletileg azonban egy üvegszál 75 Tb/s sebességre képes. Mekkora legyen a maximális csomagélettar tam ahhoz, hogy a jövő 75 Tb/s sebességű hálózataiban se fordulhasson elő kör befordulási probléma még a 64 bites sorszámok használata esetén sem? Tegyük fel, hogy a TCP-hez hasonlóan minden bájtnak saját sorszáma van. 37. Miben előnyösebb az UDP feletti RPC a tranzakciós TCP-nél? Miben előnyösebb a T/TCP az RPC-nél? 38. A 6.40.(a) ábrán látható esetben 9 csomagra van szükség egy távoli eljáráshíváshoz. Lehetségesek olyan körülmények, hogy pontosan 10 csomagra legyen szükség? 39. A 6.6.5. szakaszban azt számoltuk ki, hogy egy gigabites vonal 80 000 csomagot ad át másodpercenként a célhosztnak, amelynek mindössze 6250 utasítás ideje alatt fel kell dolgoznia azokat, így csak a processzoridő fele marad meg az alkal mazásoknak. Ebben a számításban 1500 bájtos csomagokat tételeztük fel. Végez zük el újra ezt a számolást az ARPANET-en alkalmazott csomagmérettel (128 bájt)! A csomagméretek mindkét esetben tartalmazzák a többletbájtokat is! 40. Egy 4000 km hosszú 1 Gb/s sebességű hálózaton nem a sávszélesség, hanem a késleltetés a korlátozó tényező. Tekintsünk egy MAN-t átlagosan 20 km-es for rás-cél távolsággal. Milyen adatsebességnél lesz a fény sebességéből eredő körül fordulási késleltetés az 1 kilobájtos csomag elküldési idejével egyenlő? 41. Számoljuk ki a sávszélesség és a késleltetés szorzatát a következő hálózatokra: (1) TI (1,5 Mb/s), (2) Ethernet (10 Mb/s), (3) T3 (45 Mb/s), (4) STS-3 (155 Mb/s). Feltehetjük, hogy az oda-vissza út ideje 100 ms. Ne feledjük, hogy a TCP fejrésze fenntart 16 bitet az ablakméret megjelölésére! Számításaink alapján ennek milyen következményei vannak? 42. Mennyi a sávszélesség és a késleltetés szorzata egy geoszinkron műhold 50 Mb/sos csatornáján? Mekkora legyen az ablak a csomagokban, ha azok mérete (a többletbájtokkal együtt) egységesen 1500 bájt? 43. A 6.6. ábra állományszervere távol áll a tökéletestől, lehetne még javítani rajta néhány helyen. Hajtsuk végre rajta az alábbi módosításokat: (a) Adjunk egy harmadik paramétert a klienshez, amelyben egy bájttartományt lehet megjelölni. (b) Adjunk a klienshez egy -w kapcsolót, amely lehetővé teszi, hogy az állo mányt a szerverre írjuk. 44. Módosítsuk a 6.20. ábra programját úgy, hogy hiba esetén képes legyen hibaelhá-
A SZÁLLÍTÁSI RÉTEG
625
rításra! Vezessünk be egy új reset csomagtípust, ami akkor érkezhet, ha egy öszszeköttetést mindkét fél felépített, viszont egyik sem bontott le! Ez az esemény egyszerre történik az összeköttetés mindkét végén, és azt jelenti, hogy az összes elküldött csomag vagy célba ért, vagy megsemmisült, de egyik esetben sincs már az alhálózatban. 45. írjunk programot, amely a 6.20. ábra hitel alapú rendszere helyett csúszóablakos protokollal megvalósított forgalomszabályozással rendelkező szállítási entitás pufferkezelését szimulálja! Tegyük lehetó'vé, hogy magasabb rétegbeli folyama tok véletlenszerűen létesíthessenek összeköttetést, küldhessenek adatokat és bont hassák az összeköttetést! Hogy a program egyszerű maradjon, folyjon az összes adat A gépről B gépre, visszafelé ne legyen forgalom. Kísérletezzünk a B gépnél különböző pufferfoglalási algoritmusokkal, mint például az egyes összekötteté sekhez külön pufferek rendelése, közös pufferterület alkalmazása, és mérjük meg az egyes esetekben elért teljes átbocsátóképességet! 46. Tervezzünk és valósítsunk meg egy olyan csevegő rendszert, amely több felhasz nálócsoport egyidejű csevegését teszi lehetővé! Legyen egy olyan csevegő-koor dinátor, amely egy jól ismert hálózati címen tartózkodik, UDP-t használ a klien sekkel való kommunikációhoz, létrehozza a csevegő szervereket az egyes cseve gési viszonyokhoz és karbantartja a csevegési viszonyok jegyzékét. Minden cse vegési viszonyhoz egyetlen csevegő szerver tartozik. A csevegő szerverek TCP-t használnak a kliensekkel való kommunikációhoz. A csevegő kliensprogram segít ségével a felhasználók új csevegési viszonyt indíthatnak, csatlakozhatnak egy már folyamatban levőhöz, illetve elhagyhatják a viszonyt. Tervezzük és valósítsuk meg a koordinátor, a szerver és a kliens kódját!