Az internet két fő protokollt használ a szállítási rétegben, az egyik összeköttetés-alapú, a másik összeköttetés nélküli. A két protokoll kiegészíti egymást. Az összeköttetés nélküli protokoll az UDP, amely majdnem semmit sem tesz azon túl, hogy csomagokat továbbít alkalmazások között, rájuk bízva, hogy erre egy olyan protokollt építsenek, amilyenre szükségük van. Az összeköttetés-alapú protokoll pedig a TCP, amely szinte mindent elintéz. Összeköttetéseket hoz létre, újraküldéssel megbízhatóvá teszi azokat, emellett forgalomszabályozást és torlódáskezelést is megvalósít, mindezt az alkalmazás helyett teszi.
A következő szakaszban megvizsgáljuk az UDP-t és a TCP-t. Az UDP-vel kezdjük, hiszen az az egyszerűbb. Megnézzük az UDP két tipikus alkalmazását is. Mivel az UDP egy szállítási protokoll, amely tipikusan az operációs rendszer része, és az UDP-t használó protokollok pedig a felhasználói térben (user space) futnak, ezért ezeket a protokollokat alkalmazásoknak is tekinthetjük. Az általuk használt módszerek azonban más alkalmazások részére is hasznosak lehetnek, ezért inkább a szállítási szolgáltatáshoz tartozónak tekintjük, és itt tárgyaljuk ezeket.
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áltatását, amelyek összeköttetés kiépítése nélkül akarnak IP-datagramokba beágyazott adatokat 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.27. ábrán látható. 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ó primití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). A portokra úgy gondoljunk, mint olyan postaládákra, amelyeket az alkalmazások ki tudnak bérelni csomagok fogadására. Még több mondanivalónk is lenne velük kapcsolatban, de majd a TCP tárgyalásánál foglalkozunk velük részletesebben is, ugyanis az is használ portokat. Az UDP használatának tulajdonképpen az a legnagyobb előnye a nyers IP használatával szemben, hogy a fejrészben megtalálható a feladó és a címzett portszáma is. A portszámokat tartalmazó mezők nélkül a szállítási réteg nem tudná, hogy mit kezdjen a csomaggal. A segítségükkel azonban a beágyazott szegmenseket a megfelelő alkalmazásoknak tudja kézbesíteni.
A forrás portjára elsősorban akkor van szükség, amikor választ kel küldeni a feladónak. A választ küldő folyamat úgy tudja megadni, hogy az üzenete a célgép melyik folyamatának szól, hogy a bejövő szegmens forrásport mezőjét átmásolja a kimenő 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. A legrövidebb lehetséges hossz 8 bájt, ami csak a fejrészt tartalmazza. A legnagyobb lehetséges hossz 65 515 bájt, ugyanis az IP-csomagok mérethatára nem teszi lehetővé a legnagyobb, 16 biten ábrázolható számú bájt használatát.
Az UDP ellenőrző összeg mező használata nem kötelező, de használata növeli a megbízhatóságot. Az ellenőrző összeget a fejrészre, az adatra és egy IP-álfejrészre számítják ki. Számítása során az UDP ellenőrző összeg mezőt 0-nak veszik, és az adatmezőt további 0 bájttal egészítik ki, ha az UDP-szegmens hossza mező páratlan értékű. Az ellenőrző összeg algoritmus a szegmenst 16 bites szavakra bontja, majd egyszerűen kiszámolja az egyes komplemens összegüket és végül a végeredmény egyes komplemensét veszi. Ennek következményeképpen, amikor a vevő ezt a számítást a teljes szegmensre végrehajtja, beleértve az ellenőrző összeg mezőt, az eredmény 0 lesz. Az ellenőrző összeg mező 0-t tartalmaz abban az esetben, ha nem számították ki, köszönhetően annak a szerencsés egybeesésnek, hogy a 0-nak kiszámolt összeg egyes komplemense csupa 1-esből áll. Kikapcsolni azonban nem okos dolog, kivéve, ha egyáltalán nem fontos az átvitt adatok minősége (például digitalizált beszéd).
A 6.28. ábra mutatja az álfejrészt IPv4 esetén. Az álfejrész tartalmazza a forrásgép 32 bites IPv4-címét és a célgép 32 bites IPv4-címét, az UDP-protokoll azonosítószámát (17) és egy bájtszámlálót az UDP-szegmensre (a fejrészt is beleértve). IPv6 esetén némileg különböző, de ezzel hasonló módon járunk el. Az álfejrész beszámítása az UDP ellenőrző összegbe segít megtalálni a tévesen kézbesített csomagokat, azonban megsérti a protokollhierarchiát, hiszen az IP-címek az IP-réteghez tartoznak, nem pedig az UDP-réteghez. A TCP is pontosan ezt az álfejrészt használja az ellenőrző összeg számításához.
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, torlódáskezelést vagy újraküldést egy rossz szegmens vétele esetén. Ez mind a felhasználói folyamatokon múlik. Amit elvégez: egy interfészt biztosít az IP-protokoll használatához, azzal a többletképességgel, hogy a portok használatával egyszerre több folyamatot képes demultiplexelni, valamint szükség esetén hibajelzést biztosítani végponttól végpontig. Ez minden, amit az UDP kínál.
Az olyan alkalmazásoknak, amelyeknek nem fontos a csomagforgalom, a hibakezelés vagy az időzítés precíz ellenőrzé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 kifejezetten 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 kevesebb üzenetre is van szükség (egy-egy üzenet mindkét irányban), mint egy összeköttetés felépítését igénylő protokollban (mint például a TCP-ben).
A DNS (Domain Name System – körzetné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. fejezetben lesz szó. Röviden most csak annyit, hogy minden olyan programnak, amely valamely hoszt neve (például 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üksé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.
Amikor egy hoszt üzenetet küld egy másik, távoli hosztnak, és választ is kap arra, az 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 eredmé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. Az ilyen elrendezés sokkal könnyebbé teszi a hálózati alkalmazások programozását és a használatukat is egységesebbé teszi. Elképzelhetünk például egy olyan, get_IP_address(hoszt_név) nevű eljárást, amely úgy működik, hogy egy UDP-szegmenst 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ő. Ezzel a hálózat működésének minden részlete elrejthető 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ívhassanak 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 programozó 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 alkalmazás alapjául szolgált már. A hívó folyamatot hagyományosan kliensnek, a hívott folyamatot 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ódnia, amelyet klienscsonknak (client stub) neveztek el. Ez a függvény képviseli a szervereljárást a kliens 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üggvényhívása nem helyi függvényhívás.
Egy távoli eljáráshívás gyakorlati lépéseit a 6.29. ábra szemlélteti. Az első lépésben a kliens meghívja a klienscsonkot. Ez a hívás egy helyi eljáráshívás, így a paraméterei a megszokott módon a verembe kerülnek. A második lépésben a klienscsonk a paramétereket belepakolja 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 (marshaling) nevezik. A harmadik lépésben az operációs rendszer átküldi az üzenetet a kliensgépről a szervergépre. A negyedik lépés az, hogy a szerver operációs rendszere á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 klienscsonk ugyanabban a címtérben van, a paramétereket a szokásos módon lehet átadni. 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 teljesen azonos módon használhatja azt, mivel a két eljárás ugyanabban a virtuális címzé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 átadása. Tegyük fel, hogy az első paraméter egy olyan mutató, amely egy k egész számra mutat. A klienscsonk a tényleges k számot rendezi be a csomagba, és azt küldi el a 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 vezé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, ahogy azt látni fogjuk.
A második gond az, hogy a gyengén típusos nyelvekben (például 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 csomagokba 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éterek típusát, még egy formális specifikációból vagy magából a kódból sem. Erre jó példa a printf, amely tetszőleges számú paraméterrel (de legalább eggyel) rendelkezhet, é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 lehetnek. 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ű a programozók körében, 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ő kommuniká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 szükség van néhány korlátozásra, hogy a gyakorlatban is jól működjön.
A szállítási protokollok körében az UDP jó választás az RPC megvalósítására. A legegyszerűbb esetben mind a kérések, mind a válaszok elküldhetők egyetlen UDP-csomagban, és a működése is gyors lesz. A megvalósításnak azonban egyéb eszközöket is fel kell használnia. Mivel a kérések és válaszok elveszhetnek, ezért a kliensnek egy időzítőt kell működtetnie a kérés újraküldésére. Vegyük észre, hogy a kéréseket felesleges külön nyugtázni, hiszen a válasz a kérés implicit nyugtájaként is szolgál. Néha a paraméterek vagy a válaszok olyan nagyok is lehetnek, hogy nem férnek el egyetlen UDP-szegmensben. Ilyen esetben valamilyen egyéb protokollt kell alkalmazni nagyobb üzenetek küldésére. Ha időben több kérés és válasz átlapolódhat (mint például a konkurens programozás esetében), akkor egy azonosítót kell csatolni az üzenetekhez, hogy a megfelelő kéréseket és válaszokat össze lehessen párosítani.
Magasabb szintű probléma, hogy a művelet nem feltétlenül idempotens (például nem ismételhető meg biztonságosan). A legegyszerűbb esetben a művelet idempotens, mint például a DNS-kérések és -válaszok. Ezeket a kéréseket a kliens újra és újra elküldheti, ha nem érkezik válasz, hiszen nem számít, hogy a szerver egyszer sem kapta meg a kérést, vagy a válasz veszett el. Amikor végül megérkezik a válasz, ugyanaz lesz (feltéve, hogy időközben nem frissítették a DNS-adatbázist). Mindazonáltal nem minden művelet idempotens, mert például olyan mellékhatásokat okoznak, mint mondjuk egy számláló növelése. Ilyen műveletek esetén az RPC erősebb szemantikát igényel, hogy amikor a programozó meghívja az eljárást, az ne fusson le többször. Ebben az esetben előfordulhat, hogy szükséges lehet UDP helyett egy TCP-összeköttetést felépíteni, és a kérést azon keresztül elküldeni.
A kliens–szerver RPC olyan terület, ahol az UDP széles körben használatos. Egy másik ilyen a valós idejű 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, videoszolgáltatás (video-on-demand) é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ó ötlet lenne kitalálni egy általános, több célra alkalmazható valós idejű szállítási protokollt.
Így született meg a napjainkban multimédia-alkalmazások széles körében használatos RTP (Real-Time Transport Protocol – valós idejű szállítási protokoll), amelyet az 3550-es RFC ír le. A valós idejű átvitelt két szemszögből mutatjuk be. Az első RTP-protokoll hang- és videoadatokat csomagokban továbbít. A második pedig az, amelyik a hang és videó megfelelő időben történő lejátszásához szükséges, legtöbbször a vevő oldalán történő feldolgozás révén. Ezek a feladatok a 6.30. ábrán látható módon illenek a protokollkészletbe.
Az RTP-t a felhasználó címterébe utalták, és általában az UDP-re ráépülve, az operációs rendszerben 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 csatlakozóra (socket) továbbít. A csatlakozó másik végén (az operációs rendszerben) ezekből UDP-szegmensek lesznek, amelyeket a rendszer IP-csomagokba ágyaz be, amelyek például Ethernet-keretekbe kerülnek az átvitelhez. A vevő oldalán mindez pont fordítva történik. A multimédia-alkalmazás végül multimédia-adatot kap az RTP-könyvtártól. A lejátszásáért a multimédia-alkalmazás felel. A protokollok egymásra épülését ebben a helyzetben a 6.30.(a) ábra mutatja be. A 6.30.(b) ábrán az adategységek egymásba ágyazódása látható.
6.30. ábra - (a) Az RTP helyzete az egymásra épülő protokollok között. (b) Az adategységek egymásba ágyazódása
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 az alkalmazási programmal összekapcsolták, határozottan úgy néz ki, mint egy alkalmazási protokoll. Másrészt viszont ez egy olyan általános, alkalmazásfü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 egy alkalmazási rétegben megvalósított szállítási protokoll, éppen ezért ebben a fejezetben tárgyaljuk.
Az RTP alapvető feladata az, hogy több valós idejű adatfolyamot multiplexeljen 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 az útválasztók nem kezelik különleges módon, azt az esetet kivéve, 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, és a csomagok elveszhetnek, késhetnek, megsérülhetnek stb.
Az RTP számos lehetőséget biztosít a vevők részére a multimédia-információk kezeléséhez. Egy RTP-folyamban minden csomag 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 csomagok. Ha egy csomag nem érkezik meg, az alkalmazásnak kell döntenie, mi történjen. Videoadat esetén elképzelhető, hogy kihagy egy képkockát, vagy hangadat esetén interpolációval közelíti a hiányzó értéket. Az újraküldés ebben az esetben nem jó megoldás, hiszen az újra elküldött csomag 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 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 (például 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 valós idejű alkalmazásban szükség van. Az alapötlete az, hogy a forrásgép minden csomag első mintájához egy időbélyeget rendelhet 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üknek nincs jelentése. Amint azt hamarosan megmutatjuk, ez a megoldás lehetővé teszi a célgép számára, hogy egy kis puffert használjon, és minden mintát megfelelő számú milliszekundummal a folyam kezdete után játsszon le, a mintát tartalmazó csomag megérkezési idejétől teljesen függetlenü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 sztereoadások sugárzására lehet használni, másrészt olyan filmek 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 még akkor is, ha a folyamokat elcsúszva és szeszélyesen adják és/vagy veszik.
Az RTP fejrészét a 6.31. á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 kiterjesztő szóban leírt verziószámra utaljon).
A P bit azt jelzi, hogy a csomagot 4 bájt vagy annak többszörösére kiegészítették (padding). Az utolsó kiegészítő bájt (padding byte) adja meg, hogy hány kiegészítő 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 jelentése 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.
A CC mező azt mondja 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épkeret 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 használt kódolási algoritmust (tömörítetlen 8 bites hang, MP3 stb.). Mivel ez a mező minden csomagban jelen van, a kódolást akár adás közben is meg lehet változtatni. A Sorszám mindössze egy csomagszámláló, amely minden elküldött RTP-csomagnál eggyel nő, és az elveszett blokkok felderítésére szolgál.
Az időbélyeget a folyam forrása állítja elő, hogy feljegyezze a csomag első mintájának keletkezési idejét. Ez az érték segítséget nyújthat az időzítési szórás, ún. dzsitter (jitter) csökkentésében a vevő oldalán, mivel lehetővé teszi a lejátszás függetlenítését a csomagok érkezési idejétől. A Szinkronizációs forrás azonosítója azt adja meg, hogy a csomag melyik folyamhoz tartozik. Ez a párhuzamos adatfolyamok egyetlen UDP-szegmensfolyamba való multiplexelését, majd demultiplexelését teszi lehetővé. Végül, a csomag tartalmazhat Hozzájáruló forrás azonosítója mezőket. Ezeket akkor használják, ha keverők is jelen vannak 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ást vezérlő protokollnak) neveztek el. A protokollt az RFC 3550 definiálja (az RTP mellett), és a visszacsatolást, a szinkronizációt és a felhasználói interfészt kezeli, de mintákat nem szállít.
Az első tulajdonságát arra használhatjuk, hogy visszacsatolást biztosítsunk a forrásgépeknek a késleltetésről, annak ingadozásáról (jitter), 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ő legjobb minőséget tudják biztosítani, folyamatosan alkalmazkodva a pillanatnyi körülményekhez. Például, ha az átvitel során a sávszélesség csökken vagy növekszik, 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 Adatmező típusa mező a célgépet tájékoztatja az egyes csomagokhoz használt kódolási eljárásról, így szükség esetén lehetővé teszi annak azonnali megváltoztatását.
A visszacsatolással kapcsolatban azonban felmerül egy probléma is, ugyanis az RTCP a jelentéseket minden résztvevőnek szétküldi. Egy nagy csoporttal bíró többesküldéses alkalmazás esetén az RTCP által elfoglalt sávszélesség gyorsan megnőhet. Ennek elkerülésére az RTCP-küldők annyira lecsökkentik a küldési sebességüket, hogy az összes résztvevő által elfoglalt sávszélesség ne legyen több mint mondjuk a média által elfoglalt sávszélesség 5%-a. Ennek megvalósításához minden résztvevőnek tudnia kell a média által elfoglalt sávszélességről, melyet a küldőktől tudhat meg, és a résztvevők számáról, amelyet az RTCP-jelentésekre figyelve becsülhet meg.
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 (például ASCII-szövegben) megnevezzük. Ezt az információt meg lehet jeleníteni a vevő képernyőjén, jelezve a számára, hogy ki beszél éppen.
Az RTP-ről további információ Perkins [2003] könyvében található.
Miután a médiainformáció megérkezett a vevőhöz, le kell játszani a megfelelő időben. Ez az idő általában nem az a pillanat, amikor az RTP-csomag megérkezik a vevőhöz, ugyanis a csomagok áthaladása a hálózaton más-más időt vehet igénybe. Még akkor is, ha a küldő pontosan egyenlő időközönként bocsátja a hálózatra a csomagokat, a vevőhöz különböző relatív időpontokban érkeznek meg. A késleltetésben jelentkező ilyen szórást dzsitternek (jitter) hívjuk. Még egy aránylag kis mértékű csomagdzsitter is kiábrándító médiaélményt tud okozni, például ugráló képkockákat és érthetetlen beszédet, ha a médiát megérkezésekor egyből lejátsszuk.
A megoldás a dzsitter csökkentésére az, hogy a csomagokat pufferelni kell a vevőnél, mielőtt lejátszanák azokat. A 6.32. ábrán egy olyan folyamra mutatunk példát, ahol a csomagok tekintélyes dzsitterrel érkeznek meg a vevőhöz. Az 1. csomagot a szerver másodperckor küldte, és
másodpercnél érkezik a vevőhöz. A 2. csomag nagyobb késleltetést szenved, és 2 másodperc telik el a küldése és megérkezése között. Ahogy a csomagok megérkeznek, a kliens állomás puffereli azokat.
A másodpercnél a lejátszás elindul. Ebben a pillanatban az 1-től 6-ig terjedő csomagok már a pufferben vannak, tehát egyenlő időközönként eltávolíthatjuk és lejátszhatjuk azokat a gördülékeny lejátszás érdekében. Általános esetben nem szükséges egyenlő időközöket használni, hiszen az RTP-időbélyegek megmondják, hogy mikor kell a médiát lejátszani.
Sajnos a 8. csomag annyit késik, hogy nem érhető el akkor, amikor a lejátszására kerülne a sor. Ebben az esetben két lehetőség van. A 8. csomagot figyelmen kívül hagyjuk, és a lejátszó a következő csomaggal folytatja a lejátszást. A másik lehetőség szerint a lejátszás szünetelhet addig, amíg meg nem érkezik a 8. csomag, mindezzel zavaró megakadást okozva a zenében vagy filmben. Egy élő médiaalkalmazásban, mint például egy VoIP-hívás esetén, a csomagot tipikusan eldobják, ugyanis az élő alkalmazások nem nagyon állíthatók le. Egy médiaközvetítési alkalmazás azonban a lejátszó akár le is állhat. A probléma a lejátszás kezdeti idejének eltolásával, és így nagyobb puffer alkalmazásával enyhíthető. Video- vagy hangközvetítés esetén gyakran 10 másodperc körüli puffereket használnak, hogy biztosítsák az összes (a hálózat által nem eldobott) csomag megérkezését időben. Élő alkalmazásokhoz, mint például videokonferenciához, kis pufferek használata célszerű, hogy a lejátszási késleltetést csökkentsék.
A legfontosabb tényező a gördülékeny lejátszással kapcsolatban a lejátszási pont (playback point), azaz mennyit kell várni a vevőnek, hogy a médiát lejátszhassa. Ennek eldöntése a dzsitteren múlik. A különbség a kis és nagy dzsitterű kapcsolatok között a 6.33. ábrán látható. Az átlagos késleltetés nem különbözik nagyon a két esetben, de nagy dzsitter esetén a lejátszási pontot sokkal kijjebb kell tolni, hogy a csomagok 99%-át elkapjuk, mint ha a dzsitter kicsi volna.
A helyes lejátszási pont megválasztásához az alkalmazás megmérheti a dzsittert az RTP-időbélyegek és a beérkezési idők különbségének a figyelésével. Minden különbség egy késleltetési mintát ad (növelve egy tetszőleges, de állandó késleltetéssel). A késleltetés azonban az idővel változhat, más, versengő forgalmak és változó útvonalak következtében. Hogy az alkalmazások alkalmazkodjanak ehhez a változáshoz, a lejátszási pontjukat eltolhatják a futás során. A lejátszási pont helytelen módon történő megváltoztatása azonban észrevehető hibát okozhat a felhasználónál. Hang esetén az jelenthet megoldást, ha a lejátszási pontot a beszédlöketek (talkspurt) között, azaz a beszédszünetekben változtatjuk meg. Senki sem fogja észrevenni a különbséget egy rövid és egy hosszabb szünet között. Az RTP éppen ezért lehetővé teszi az alkalmazások számára, hogy az M jelölőbittel megjelöljék a beszédlöketek kezdetét.
Az élő alkalmazások nem tudják elviselni, ha a média lejátszásáig tartó abszolút késleltetés túl nagy. Ha már eddig is egy közvetlen útvonalat használunk, semmi sem tudja csökkenteni a terjedési késleltetést. A lejátszási pontot beljebb lehet húzni, ha elfogadjuk, hogy a csomagok nagyobb hányada fog késve megérkezni a lejátszáshoz. Ha ez nem elfogadható, akkor az egyetlen módja a lejátszási pont beljebb húzásának, ha a dzsittert jobb szolgáltatásminőségi módszerek használatával csökkentjük, például sürgős továbbítást használunk differenciált szolgáltatásokkal. Azaz, egy jobb minőségű hálózatot kell használnunk.