Menu Zavrieť

3.4. Transportný protokol TCP

TCP: transmission control protocolRFC 793RFC 1122RFC 1323RFC 2018RFC 2581

Protokol TCP je najpoužívanejším transportným protokolom na internete. Na rozdiel od protokolu UDP realizuje potvrdzovaný prenos dát medzi soketmi. To znamená, že odoslané dáta z jedného soketu sa dostanú všetky a v rovnakom poradí do druhého soketu. Nakoľko aj protokol TCP využíva nespoľahlivý prenos dát nižších vrstiev (pakety sa môžu stratiť alebo prísť v inom poradí) musí zavádzať mechanizmy na overovanie toho, či segmenty došli všetky a nepoškodené. V prípade, že nie, tak musí vynútiť ich opätovné zaslanie. Tomuto princípu je podriadený takmer celý návrh protokolu.

Hlavička TCP segmentu je voči hlavičke UDP segmentu o dosť bohatšia.

Význam jednotlivých častí TCP hlavičky:

  • source port – zdrojový port alebo aj port odosielateľa tohto segmentu
  • destination port – cieľový port alebo aj port príjemcu tohto segmentu
  • sequence number – sekvenčné číslo – Určuje poradie prvého bajtu segmentu v prúde dát aplikačnej vrstvy. Číslovanie prúdu dát sa začína náhodným číslom a po dosiahnutí maximálnej hodnoty 232-1 pokračuje opäť od nuly.
  • acknowledgement number – číslo potvrdenia alebo číslo ACK – Určuje očakávané sekvenčné číslo nasledujúceho, doteraz nedoručeného segmentu.
  • data offset – dĺžka hlavičky v bajtoch
  • reserved – rezervované – neobsahuje nič.
  • príznaky – Ak majú hodnotu 1 tak sú aktivované, ak 0 tak sú vypnuté. Popíšeme čo znamená, keď majú hodnotu 1.
    • URG zo slova urgentné – Niektoré aplikačné protokoly môžu využívať tento príznak na označenie toho, že sa prerušuje zasielaný prúd dát, aby sa oznámila nejaká správa. Napríklad počas posielania veľkého súboru chceme robiť nejaké riadiace príkazy (prerušiť posielanie, informovať o nejakej udalosti na stanici odosielateľa). Hodnota urgent pointer je v tomto prípade nastavená na pozíciu posledného bajtu urgentnej správy v segmente. (urgentné správy sú skôr výnimkou)
    • ACK – Hodnota „acknowledgement number“, teda číslo potvrdenia, je relevantná.
    • PSH zo slova „push“ t. j. posuň. – Hovorí, že dáta majú byť okamžite posunuté aplikačnej vrstve. Napríklad to môže znamenať, že ide o posledný segment aktuálne odosielanej správy.
    • RST – „reset“ – násilné ukončenie spojenia
    • SYN – „synchronize“ – označenie segmentov použitých pri začiatku spojenia (viď nižšie)
    • FIN – „finalize“ – označenie segmentov použitých pri uzatváraní spojenia (viď nižšie)
  • window – veľkosť okna – Buffer príjemcu obsahuje prijatý súvislý blok dát, ktoré aplikačná vrstva ešte nespracovala, a zvyšné miesto zvané okno príjemcu. Veľkosť okna príjemcu je zasielaná v hodnote window odosielateľovi. Keďže oba komunikujúce sokety môžu byť príjemcovia aj odosielatelia, každý má svoje okno prijatých správ.
  • checksum – kontrolný súčet – Má rovnaké využitie ako pri UDP segmentoch.
  • urgent pointer – poradie bajtu, kde končia urgentné dáta
  • options and padding – voľby a výplň do násobku 16 bitov – Dodatočné nastavenia, využité na:
    • dohadovanie MSS (maximum segment size) pri nadväzovaní spojenia. MSS je maximálna veľkosť dát v tele TCP segmentu. Keby sa hodnota neuviedla, MSS by bolo 536 bajtov, bežne sa však používa 1460 bajtov.
    • Window Scale (WSCALE) – násobok veľkosti okna – Hodnota „window“ má iba 16 bitov, čo znamená, že bez násobku by mohla byť maximálna veľkosť okna 64 kB (maximálne číslo zapísateľné 16 bitmi je 65535). Pri prenose na veľké vzdialenosti je potrebné väčšie okno, aby sa využila celá šírka pásma. Pomocou tejto voľby vieme pri nadväzovaní spojenia povedať, akým násobkom dvojky sa má hodnota „window“ násobiť. Takto vieme dosiahnuť veľkosť okna až do 1 GB.
    • Ďalšie dodatočné nastavenia si môžete pozrieť v RFC 1323 alebo stručnejšie napríklad v http://www.securityfocus.com/infocus/1223.

3.4.1  Manažment nadviazania spojenia

Pred samotným odosielaním dát je potrebné, aby sa nadviazalo spojenie. Pri nadväzovaní spojenia si obidva konce musia pripraviť niekoľko premenných, na základe ktorých sa počas prenosu bude riadiť prijímanie a odosielanie dát, potvrdzovanie prijatých segmentov a opätovné preposielanie stratených segmentov. V popise nadviazania spojenia uvádzame aj hodnoty window, MSS a WSCALE, ktoré sú bližšie vysvetlené v časti Prenos dát.

Nadviazanie TCP spojenia je realizované trojfázovým „potrasením rúk“ (three-way handshake).

  1. Odoslanie SYN segmentu k serveru. V prvej fáze si klient vygeneruje náhodné úvodné sekvenčné číslo, od ktorého sa začnú číslovať bajty odosielaných dát z aplikačnej vrstvy. Okrem tohto čísla si vytvorí aj buffre na okná odosielaných a prijatých segmentov nejakej úvodnej veľkosti. Úvodné sekvenčné číslo sa vloží do hlavičky. Samozrejme sa do hlavičky napíše číslo zdrojového a cieľového portu a ostatné súčasti hlavičky. Okrem toho je v hlavičke ešte nastavený príznak SYN na 1 označujúci, že ide o prvý segment nadväzovania spojenia. V časti options sa nastaví MSS a WSCALE. Hodnota položky window sa nastaví podľa veľkosti vytvoreného okna príjemcu a hodnoty WSCALE.
  2. Odoslanie SYN/ACK segmentu ku klientovi, t. j. sú zapnuté príznaky SYN aj ACK. Serverový soket akceptoval spojenie a vytvoril nový soket, ktorý bude komunikovať s týmto klientom. K tomuto novému soketu sa tiež vygenerujú úvodné premenné, t.j náhodné úvodné sekvenčné číslo pre dáta odosielané na klienta, buffre pre okná prijatých a odoslaných segmentov. V hlavičke sa nastaví cieľový port podľa zdrojového portu z prijatého segmentu a zdrojový port podľa cieľového portu prijatého segmentu. Sekvenčné číslo je serverom vygenerované jeho úvodné sekvenčné číslo. Číslo potvrdenia je o 1 väčšie ako sekvenčné číslo z prijatého segmentu. Z toho vieme napríklad to, že v najbližšom prijatom segmente očakáva, že ako sekvenčné číslo bude uvedené práve toto číslo. V časti options nastaví svoje MSS a WSCALE. Hodnotu položky window nastaví podľa veľkosti vytvoreného okna príjemcu a hodnoty WSCALE.
  3. Odoslanie ACK segmentu k serveru. V poslednej, tretej fáze klient pošle serveru segment so zapnutým ACK príznakom. Sekvenčné číslo je o 1 väčšie ako v prvom segmente a číslo potvrdenia je o 1 väčšie ako sekvenčné číslo v segmente, ktorý prišiel zo servera.

Po inicializácii môže klient aj server už posielať segmenty obsahujúce dáta aplikačnej vrstvy. Klient ich môže začať posielať už ako súčasť ACK segmentu tretej fázy nadväzovania spojenia. Server až po prijatí tohto ACK segmentu. Kto začne posielať dáta, závisí od aplikačného protokolu (môžu aj obaja súčasne).

Príklad nadviazania spojenia (uvádzame iba podstatné položky):

  1. Klient posiela: source port: 12345, destination port: 80, sequence number 300000 (náhodne vygenerované), acknowledgement number: 0, SYN=1, ACK=0
  2. Server posiela: source port: 80, destination port: 12345, sequence number 200000 (náhodne vygenerované), acknowledgement number: 300001, SYN=1, ACK=1
  3. Klient posiela: source port: 12345, destination port: 80, sequence number 300001, acknowledgement number: 200001, SYN=0, ACK=1

Poznámka: Úvodné sekvenčné čísla sa vyberajú náhodne, aby sa zabránilo pomiešaniu dvoch po sebe nasledujúcich rovnakých spojeniach, ale aj aby sa zabránilo známemu útoku založenom na tom, že ak by som dopredu poznal sekvenčné čísla TCP komunikácie, mohol by som ľahko filtrovať TCP segmenty vedúce okolo môjho načúvacieho zariadenia a vkladať svoje segmenty do už existujúcich spojení, prípadne úplne prevziať kontrolu nad otvoreným spojením, alebo tiež zákerne uzatvárať otvorené spojenia.

3.4.2  Manažment ukončenia spojenia

Korektné ukončenie TCP spojenia je potrebné urobiť tak, aby obe komunikujúce strany vedeli, že druhá strana vie, že spojenie sa končí. Požiadavka na ukončenie spojenia môže prísť od klienta aj od servera – často záleží aj na aplikačnom protokole, lebo TCP protokol ich už nerozlišuje.

  1. Prvá stanica (iniciátor ukončenia) posiela FIN segment. Tento segment má nastavený príznak FIN na 1. Príznak ACK môže byť 0 alebo 1 podľa toho, či bol pred tým zaslaný nejaký dátový segment z druhej stanice.
  2. Druhá stanica posiela ACK segment, t.j. potvrdenie prijatia FIN (alebo FIN/ACK) segmentu. Keď druhá stanica ešte potrebuje doposielať nejaké dáta, tak aj po prijatí FIN segmentu môže pokračovať s posielaním dát.
  3. Druhá stanica posiela FIN/ACK segment, t.j. príznaky FIN aj ACK sú nastavené na 1.
  4. Prvá stanica odosiela ACK segment. FIN je nastavené na 0 a ACK na 1. V tejto chvíli prvá stanica zapne časovač na „dosť dlhý čas“ (často 30 s, minúta, alebo dve minúty). Ak počas tohto času dostane FIN/ACK segment, znamená to, že sa jeho ACK segment stratil a posiela ho znova (viď. Prenos dát nižšie). Ak nedostane už žiadne segmenty, po zastavení časovača sa soket definitívne uzatvára.
  5. Druhá stanica prijme ACK segment. Jej soket sa zavrie.

3.4.3  Prenos dát (metóda sliding window – posuvné okno)

TCP protokol poskytuje sieťovým aplikáciám spoľahlivý, potvrdzovaný prenos dát. Využíva na to nespoľahlivý prenos svojich segmentov sieťovým protokolom IP. IP datagramy sa môžu stratiť alebo prísť do cieľovej stanice v inom poradí, než v akom boli vyslané.

Sekvenčné čísla

Segmenty vytvárané transportnou vrstvou sa skladajú z hlavičky a tela. Telo segmentu má určenú maximálnu veľkosť hodnotou MSS (maximum segment size) dohodnutú pri nadväzovaní spojenia. Keď aplikačná vrstva pošle správu (dáta), ktorá je väčšia ako MSS, je nutné túto správu rozdeliť do viacerých segmentov. Každý z týchto segmentov má jednoznačne pridelené sekvenčné číslo, ktoré sa uvádza v hlavičke segmentu.

Napríklad, ak MSS je 1400 a správa má 5000 bajtov, vytvoria sa 4 segmenty. V tele prvého segmentu sú bajty 1 až 1400, v tele druhého 1401 až 2800, v tele tretieho 2801 až 4200 a v tele štvrtého 4201 až 5000. Ak ide o úplne prvú správu spojenia a pri nadväzovaní spojenia sa vytvorilo úvodné sekvenčné číslo napr. 300000, tak naše segmenty majú sekvenčné čísla 300001, 301401, 302801 a 304201. Ak by nešlo o prvú správu, tak k týmto číslam by sme ešte museli pripočítať počet pred tým odoslaných bajtov aplikačnej vrstvy cez tento soket. Napríklad, ak úvodné sekvenčné je číslo 300000, a pred touto správou sa už odoslali správy, ktoré mali dokopy 10000 bajtov, tak tieto naše štyri segmenty by mali sekvenčné čísla 310001, 311401, 312801 a 314201. Maximálne sekvenčné číslo je 232-1, nasledujúce segmenty sú už číslované modulo 232.

Aby sme to zovšeobecnili, keď úvodné sekvenčné číslo označíme USČ, veľkosť dát vo všetkých predchádzajúcich segmentoch dokopy označíme ako D, tak sekvenčné číslo ďalšieho dátového segmentu sa rovná (USČ + D) mod 232-1. Reálne sa to počíta iba zo súčtu sekvenčného čísla predchádzajúceho segmentu a veľkosti dát v ňom modulo 232, ktorý sa uloží v premennej ďalšieSekvenčnéČíslo.

Kumulatívne potvrdenie

Keďže odosielateľ dát musí byť nejakým spôsobom informovaný príjemcom o tom, ktoré odoslané segmenty došli k príjemcovi, príjemca mu to oznamuje cez čísla potvrdenia. V protokole TCP sa používa takzvané kumulatívne potvrdenie: ak odosielateľ dostane potvrdzovací segment s číslom potvrdenia X, tak to znamená, že príjemca prijal v poriadku všetky segmenty s číslami menšími ako X a očakáva, že mu príde segment so sekvenčným číslom X, ktorý zatiaľ nedostal.

Okno odosielateľa

Ak by sme museli po každom odoslanom dátovom segmente čakať na príslušné potvrdenie ešte pred odoslaním ďalšieho dátového segmentu, mohli by sme byť výrazne obmedzovaní pri komunikácii na veľké vzdialenosti. Nie je výnimkou, že čas od odoslania segmentu až po doručenie príslušného potvrdenia, nazývaný „round trip time“ (RTT), môže byť aj výrazne viac ako 100 ms. Keďže veľkosť tela segmentu je typicky zhruba 1,5 kB, vedeli by sme pri RTT 100 ms preniesť iba asi 15 kB za sekundu, bez ohľadu na šírku pásma.

Z tohto dôvodu TCP umožňuje poslať viac segmentov bez doručenia potvrdenia istého množstva pred tým odoslaných segmentov. Tomuto „hromadnému“ spôsobu odosielania hovoríme pipelining. Stále však platí, že potrebujeme mať potvrdené prijatie všetkých odoslaných segmentov, len k tomu dochádza oneskorene. Obe komunikujúce stanice majú svoje okno odosielateľa – vyhradené miesto v pamäti, v ktorom si uchovávajú postupnosť odoslaných, ale zatiaľ nepotvrdených segmentov. Odosielateľ môže odoslať maximálne toľko segmentov, koľko vojde do okna odosielateľa bez toho, aby hociktorý z nich bol potvrdený. Keď príde potvrdenie, tak z okna sa odstránia všetky segmenty so sekvenčným číslom menším ako číslo potvrdenia. Okno sa akoby posunie v rade odosielaných segmentov a vznikne tak priestor pre nové segmenty, ktoré sa môžu uložiť do okna odosielateľa a odoslať príjemcovi, ak aplikácia má ešte nejaké časti správy, ktoré neboli odoslané.

Napríklad máme okno veľkosti 3 segmentov a chceme odoslať segmenty, ktoré majú sekvenčné čísla 300001, 301401, 302801 a 304201, môžeme odoslať prvé tri, ktoré potom „čakajú“ v okne odosielateľa na potvrdenie. Posledný segment poslať nemôžeme, lebo nevošiel do okna odosielateľa. Ak teraz príde napríklad potvrdenie s číslom 302801, odstránime z okna prvé dva segmenty, pretože TCP protokol používa kumulatívne potvrdenie. Takže keď nám príde potvrdenie s číslom 302801, vieme, že príjemca už prijal všetky segmenty so sekvenčnými číslami menšími ako 302801. Posunutím okna, teda vyhodením segmentov so sekvenčnými číslami 300001 a 301401, vznikne priestor v okne aj pre segment so sekvenčným číslom 304201, takže ho môžeme odoslať. Okno teda bude obsahovať segmenty so sekvenčnými číslami 302801 a 304201 až pokiaľ nepríde nové potvrdenie s číslom potvrdenia väčším ako 302801. Druhá možnosť je, že aplikačná vrstva bude medzi tým už chcieť poslať ďalšiu správu (nové dáta), kedy by sa dalo využiť ešte jedno miesto v okne odosielateľa.

Okno príjemcu

Obe strany komunikácie majú okrem okna odosielateľa aj okno príjemcu. Prvá pozícia v okne príjemcu predstavuje najstarší doteraz nedoručený segment v postupnosti prijímaných segmentov. Jeho číslo segmentu označme rcv_base. V okne príjemcu sa uchovávajú iba segmenty doručené mimo poradia (s väčšími sekvenčnými číslami ako rcv_base). Celý úvodný súvislý blok doručených segmentov, teda všetky segmenty so sekvenčnými číslami menšími ako rcv_base už boli odoslané príjemcovmu soketu v minulosti. V prípade, že príde segment so sekvenčným číslom rcv_base, sú príjemcovmu soketu odoslané okrem dát z tohto segmentu aj dáta všetkých nasledujúcich segmentov z okna príjemcu až po prvý nedoručený segment, ktorého sekvenčné číslo sa stane novou hodnotou rcv_base.

Pozrime sa na príklad znázornený vyššie. Na obrázku a) máme hodnotu rcv_base = 200000 a v okne príjemcu 2 prijaté segmenty mimo poradia, kde prvý má sekvenčné číslo 202000 a veľkosť 500 bajtov a druhý má sekvenčné číslo 203000 a veľkosť 1000. Dáta všetkých segmentov s číslami menšími ako 200000 už boli odoslané príjemcovmu soketu. Ešte nám chýbajú dáta 200000 až 201999 a 202500 až 202999.

Obrázok b) znázorňuje, že prišiel datagram so sekvenčným číslom 200000 a veľkosťou 1000 bajtov. Pošleme dáta z tohto datagramu príjemcovmu soketu a zvýšime hodnotu rcv_base na 201000. Potvrdenie zaslané odosielateľovi bude mať číslo potvrdenia 201000. Teraz máme chýbajúce dáta 201000 až 201999 a 202500 až 202999.

Na obrázku c) vidíme, že nám prišiel datagram so sekvenčným číslom 201000 a veľkosťou 1000 bajtov, pošleme teda dáta z tohto datagramu príjemcovmu soketu, ale už aj spolu s dátami z datagramu so sekvenčným číslom 202000, lebo sme vyplnili celú oblasť chýbajúcich dát až po bajt s číslom 202499. Datagram so sekvenčným číslom 202000 následne odstránime z okna príjemcu. Hodnota rcv_base sa zvýši na 202500 (prvý neprijatý bajt). Potvrdenie zaslané odosielateľovi bude mať číslo potvrdenia tiež 202500. Teraz máme chýbajúce dáta už iba 202500 až 202999. Ak by hocikedy prišiel datagram so sekvenčným číslom väčším ako rcv_base, je zaradený do okna príjemcu.

Opätovné posielanie segmentov

Vieme, že sieťová vrstva neposkytuje spoľahlivý prenos dát. To znamená, že ak chceme zabezpečiť spoľahlivý prenos dát, musíme sa vedieť zotaviť zo straty segmentu. Hlavný problém je v identifikovaní samotnej udalosti straty segmentu, ktorá sa stala niekde na ceste medzi cieľovými stanicami. Stratu musí registrovať hlavne odosielateľ, aby vedel, že má opätovne poslať stratený segment. Odosielateľ považuje za stratu jednu z dvoch udalostí:

  • Prišli aspoň 3 potvrdenia s rovnakým číslom potvrdenia. Vieme, že TCP protokol používa kumulatívne potvrdenie, t. j. vždy sa potvrdzuje číslom rcv_base, čo je číslo prvého bajtu najstaršieho nedoručeného segmentu. Ak teda u príjemcu nastane situácia, že mu prídu segmenty s väčším sekvenčným číslom ako rcv_base, tak sa každý z nich potvrdzuje číslom rcv_base. Odosielateľ pri prijatí viacerých potvrdení s rovnakým číslom potvrdenia teda vydedukuje, že segment so sekvenčným číslom rovným tomuto mnohonásobnému číslu potvrdenia je potrebné poslať znova, lebo príjemca ho neprijal skôr ako segmenty s väčšími číslami, a teda sa pravdepodobne stratil (mohol aj iba meškať, lebo išiel pomalšou trasou po internete). Preposlanie na základe 3 rovnakých potvrdení nazývame rýchle preposlanie.
  • Nastal timeout. Odosielateľ zapína časovač vždy vtedy, keď sa zmení najstarší segment v okne odosielaných segmentov. Toto môže nastať, ak vložíme práve odoslaný segment do prázdneho okna odosielateľa, alebo ak došlo potvrdenie, ktoré potvrdilo doručenie dovtedy najstaršieho segmentu (niektoré segmenty sa odstránili) a iný segment v okne sa stal najstarším. Časovač sa vždy zapne s nejakým intervalom časovača (TimeOutInterval). Ak počas tohto intervalu nedôjde potvrdenie, ktoré by potvrdilo doručenie najstaršieho segmentu, nastane timeout a tento segment sa pošle znova.

Na nasledujúcom obrázku môžeme vidieť situáciu, kde okno odosielateľa (Stanica A) má kapacitu na 5 segmentov. Druhý segment so sekvenčným číslom 100 sa stratil na ceste k príjemcovi. Príjemca (Stanica B) dostane segmenty s číslami 92, 120, 135 a 141. Segment s číslom 92 pošle aplikačnej vrstve a očakáva segment so sekvenčným číslom 100, lebo veľkosť dát v tomto segmente bola 8 bajtov. Pošle teda potvrdenie s číslom potvrdenia 100. Následne dostane segmenty so sekvenčnými číslami 120, 135 a 141. Keďže stále očakáva, ako najstarší neprijatý segment, segment so sekvenčným číslom 100, uloží si tieto nové segmenty prijaté mimo poradia do okna príjemcu a pre každý z nich pošle potvrdenie s číslom potvrdenia 100. Odosielateľ dát (Stanica A) dostane viackrát potvrdenie s rovnakým číslom potvrdenia a vykoná rýchle preposlanie segmentu so sekvenčným číslom 100 ešte pred timeoutom po troch potvrdeniach s rovnakým číslom potvrdenia.

Niektoré implementácie protokolu TCP robia ešte to, že ak príjemca dostane segment mimo poradia, pošle ihneď dve rovnaké potvrdenia, aby sa ešte viac urýchlilo preposlanie strateného segmentu.

Nastavenie timeout intervalu

Hodnota timeout intervalu pre časovač by mala byť dostatočne veľká, aby nedochádzalo k zbytočným opätovným poslaniam segmentov, pokiaľ nedošlo k strate, len segmenty mali väčšie zdržanie. Tiež by nemala byť príliš veľká, aby sme zabránili pomalým reakciám na stratu segmentu.

Už sme povedali, že čas, za ktorý odíde segment k príjemcovi a vráti sa potvrdenie, nazývame round trip time (RTT). Je jasné, že timeout by mal byť väčší ako RTT. V prípade, že by RTT bol vždy rovnaký, stačilo by nastaviť timeout o málo (napr. jednu nanosekundu 🙂 ) viac ako RTT a vedeli by sme že po uplynutí timeout intervalu určite nastala strata.

RTT je zakaždým iný. Na určenie veľkosti okna potrebujeme snímkovať každý RTT. Označme posledný odmeraný RTT ako SampleRTT. Keďže jednotlivé RTT sa menia často výrazne, potrebujeme nejakú stabilnejšiu hodnotu, ktorá je podobná priemeru „posledných“ RTT. Na tento účel sa používa premenná EstimatedRTT, predstavujúca očakávanú hodnotu RTT, definovaná nasledovne.

EstimatedRTT = (1-a)*EstimatedRTT + a*SampleRTT, kde a je typicky 0,125.

Hodnota EstimatedRTT ešte nie je vhodná na to, aby predstavovala veľkosť okna, pretože približne polovica segmentov by bola potvrdená neskoro, lebo by už došlo k timeoutu a preposlaniu.

Odchýlka EstimatedRTT od poslednej hodnoty SampleRTT sa dá vypočítať ako absolútna hodnota rozdielu SampleRTT – EstimatedRTT. Na určenie očakávanej odchýlky , označenej DevRTT, použijeme vzorec:

DevRTT = (1-b)*DevRTT + b* |SampleRTT – EstimatedRTT|, kde b je typicky 0,25.

Ako výsledný timeout interval časovača sa potom používa: TimeOutInterval = EstimatedRTT + 4*DevRTT

Tým získame nový interval časovača pri každom prijatí potvrdenia.

3.4.4  Kontrola toku dát

Cieľom kontroly toku dát je zabrániť situácii, keď príjemca nestíha spracovávať prijaté dáta. Typickým príkladom by mohlo byť to, že príjemca všetky prijaté dáta posiela rovno na disk a v prípade plného využitia spojenia so šírkou pásma 1 Gb/s by disk nestíhal tieto dáta ukladať.

Každý soket má pridelené pamäťové miesto (buffer príjemcu). Na obrázku nižšie je veľkosť tohto pamäťového miesta označená ako RcvBuffer. Toto pamäťové miesto sa delí na okno príjemcu (Spare room veľkosti RcvWindow) a na rad segmentov určených na spracovanie prijímajúcou aplikáciou (TCP data in buffer), ktoré môže, ale ešte nestihla spracovať. Keď aplikácia nestíha spracovávať dáta, zväčšuje sa rad segmentov pre prijímajúcu aplikáciu a zmenšuje sa okno príjemcu.

Keby sa veľkosť okna príjemcu zmenšila na nulu, začali by sa zahadzovať správne doručené segmenty. Aby sa tomu zabránilo, oba komunikujúce sokety sa navzájom informujú o veľkosti svojho okna príjemcu (hodnota RcvWindow) v hlavičke TCP segmentu v časti window.

Odosielateľ si podľa tejto hodnoty môže zmenšiť svoje okno odosielateľa, keďže veľkosť okna odosielateľa určuje maximálny počet odoslaných segmentov za čas RTT. Ľudovo povedané, určuje „najvyššiu povolenú rýchlosť“ odosielania, teda zmenšenie okna odosielateľa zmenší aj prenosovú rýchlosť a tým umožní príjemcovi spracovanie takou rýchlosťou, akú zvládne.

3.4.5  Kontrola zahltenia siete

Pri intenzívnej komunikácii viacerých staníc cez jeden router a jeden spoj sa môže stať, že sa výstupný buffer routra na niektorom jeho rozhraní môže zahltiť a začne zahadzovať pakety. Jeden z najjednoduchších prípadov zahltenia je zobrazený na nasledujúcom obrázku.

Stačí, ak všetky tri spoje majú rovnakú šírku pásma povedzme 100 Mb/s. Ak by obe vysielajúce stanice vysielali rýchlosťou 100Mb/s, router by nemohol vysielať rýchlosťou 200 Mb/s, nutne by sa zahltil a začal pakety zahadzovať.

Už sme si povedali, že na zníženie rýchlosti odosielania je potrebné zmenšiť okno odosielateľa. Pri kontrole zahltenia budeme používať rovnaký princíp. Budeme používať premennú Congwin (Congestion window = okno zahltenia) na označenie maximálnej povolenej veľkosti okna odosielateľa určenej mechanizmom kontroly zahltenia. V tejto kapitole budeme predpokladať, že veľkosť okna prijatých správ príjemcu je vždy väčšia ako Congwin. Reálne je veľkosť okna odosielateľa minimum hodnôt Congwin a window z hlavičky posledného prijatého TCP segmentu.

Protokol TCP odhalí zahltenie siete iba tak, že zaregistruje strácanie segmentov. To znamená, že nastane situácia, keď musí opätovne posielať segmenty: buď došli 3 segmenty s rovnakým číslom potvrdenia alebo nastal timeout. TCP to vezme ako signál na to, že je potrebné spomaliť odosielanie segmentov, teda zmenšiť Congwin. Na druhej strane, ak sa segmenty nestrácajú, tak je možné, že rýchlosť prenosu sa môže zvýšiť a mohlo by sa vyplatiť veľkosť okna, t.j. Congwin zväčšovať.

Presne na týchto pokusoch o zväčšovanie okna a zmenšení okna pri strate je založený algoritmus kontroly zahltenia siete v TCP, ktorý sa skladá z 3 hlavných častí: 1, Algoritmus AIMD = additive increase, multiplicative decrease (zväčšovanie pripočítavaním, zmenšovanie delením), 2, Algoritmus Slow start (pomalý štart) a 3, prepínanie medzi týmito postupmi.

Algoritmus AIMD funguje tak, že pokiaľ neevidujeme stratu, tak zväčšíme Congwin o maximálnu veľkosť jedného segmentu (MSS) vždy po tom, keď príde Congwin potvrdení. Napríklad majme Congwin rovné 4 MSS. Odošleme 4 segmenty a čakáme na ich potvrdenie. Po potvrdení každého segmentu z okna odosielateľa zvýšime veľkosť okna Congwin o 1/4 MSS. Po potvrdení všetkých 4 segmentov sa nám teda zväčší Congwin o 1 MSS na 5 MSS. Pri potvrdeniach ďalších 5 segmentov budeme zväčšovať Congwin o 1/5 MSS. To znamená, že ak je Congwin rovný 4 MSS, odošleme 4 segmenty a po ich potvrdení zväčšíme Congwin na 5 MSS. Potom odošleme 5 segmentov a po ich potvrdení zväčšíme Congwin na 6 MSS, a tak ďalej. Takže po potvrdení celého predchádzajúceho okna segmentov zväčšujeme Congwin o 1 MSS.

Takéto zväčšovanie uskutočňujeme dovtedy, pokiaľ nezaregistrujeme stratu segmentu. V takom prípade samozrejme pošleme opäť stratený segment, ale hlavne zmenšíme Congwin na polovicu – zmenšovanie delením. Keby naďalej dochádzalo k stratám, delíme stále na polovicu až na minimum Congwin = 1 MSS.

Algoritmus Slow start sa používa na rýchle zistenie priepustnosti spojenia. Napríklad, ak by sme mali reálnu priepustnosť 100 Mb/s a MSS rovné 1500 bajtov = 12000 bitov, tak by sme na dosiahnutie plnej prenosovej rýchlosti spoja (a teda na prvú stratu) čakali 83 cyklov zväčšovania Congwin v algoritme AIMD. Pri RTT 200 ms by to bolo až 16 sekúnd. Algoritmus Slow start zväčšuje Congwin o 1 MSS pri potvrdení každého segmentu. To znamená, že po odoslaní a potvrdení celého okna segmentov veľkosť okna zdvojnásobí. Slow start začína pri veľkosti okna 1 MSS. Po potvrdení prvého odoslaného segmentu má veľkosť 2 MSS, po potvrdení nasledujúcich 2 segmentov má veľkosť už 4 MSS a tak ďalej. V našom predchádzajúcom príklade by sme dosiahli maximálnu priepustnosť (a prvú stratu) už po siedmom odoslaní a potvrdení celého okna, t. j. po 1,4 sekunde.

Reálne sa však takýto exponenciálny nárast veľkosti okna reguluje hodnotou Threshold (v preklade prah). Keď pri algoritme Slow start dosiahneme veľkosť okna rovnú hodnote Threshold, prechádzame na algoritmus AIMDThreshold sa na začiatku nastavuje v závislosti od šírky pásma pripojenia tak, aby sa exponenciálnym nárastom nepresiahla šírka pásma. Ak dôjde k strate, mení sa hodnota Threshold na Congwin/2 .

Prepínanie medzi týmito dvoma prístupmi a nastavovanie premenných Congwin a Threshold je riadené udalosťami. Na začiatku sme v stave Slow start, veľkosť okna Congwin je 1 a poslali sme prvý segment. Môžu nastať tieto udalosti:

  • prišlo potvrdenie pre 1 segment v okne odosielateľa
    • ak sme v stave Slow start
      • Congwin = Congwin + MSS

      Ak Congwin > Threshold, prepni sa do stavu AIMD

    • ak sme v stave AIMD
      • pocetCelychMSS = Congwin celočíselne vydelené číslom MSS

      Congwin = Congwin + MSS/ pocetCelychMSS

  • strata identifikovaná troma segmentmi s rovnakým číslom potvrdenia
    • Threshold = Congwin/2

    Congwin = Threshold prepni sa do stavu AIMD

  • strata identifikovaná timeout-om
    • Threshold = Congwin/2

    Congwin = 1 MSS prepni sa do stavu Slow start

3.4.6  Priepustnosť TCP protokolu

Optimisticky predpokladajme, že k strate dochádza vždy pri rovnakej veľkosti okna W a že strata nie je nikdy identifikovaná timeout-om. Ak ignorujeme aj úvodný algoritmus Slow start, tak veľkosť okna sa pohybuje medzi W/2 a W. Keď si uvedomíme, že veľkosť okna v čase rovnomerne rastie medzi týmito dvoma hodnotami, potom klesne na W/2 a opäť pomaly rastie k W, tak zistíme, že priemerná veľkosť okna je 0,75 W a teda priepustnosť 0,75 W/ RTT.

Ďalšou zaujímavou skutočnosťou je to, že ak by sme chceli posielať dáta rýchlosťou napríklad 10 Gb/s pri RTT 0,1 sekundy, potrebovali by sme okno veľkosti 109 b = 128 MB. Keď sa pozrieme na TCP hlavičku, máme k dispozícii 16 bitov na informáciu o veľkosti voľnej časti okna príjemcu, ktoré by malo byť väčšie alebo rovné veľkosti okna odosielateľa. Pomocou 16 bitov však vieme vyjadriť najväčšie číslo iba 216-1=65535 B = 64 KiB. Pri nadväzovaní spojenia sa však cieľové stanice môžu dohodnúť na mocnine dvojky, ktorým sa bude počas celej komunikácie násobiť číslo uvedené v hlavičke v časti window. Táto mocnina dvojky sa uvádza v premennej WSCALE. Najväčšia hodnota WSCALE je 14, teda maximálna veľkosť okna môže byť až 214 * (216-1) t. j. takmer 1 GiB.

3.4.7  Spravodlivosť TCP protokolu

Spravodlivosť TCP protokolu znamená, že ak cez rovnaký spoj (úzke miesto) s prenosovou rýchlosťou R prechádza k TCP spojení (a žiadna ďalšia komunikácia), tak každé z týchto TCP spojení využíva rovnakú prenosovú rýchlosť R/k. Samotná spoľahlivosť je dôsledkom kontroly zahltenia siete protokolu TCP, ktorá spôsobí, že pri strate segmentu sa zníži rýchlosť odosielania zmenšením okna odosielateľa na polovicu (menej často na veľkosť 1 MSS). Pri odosielaní paketov cez úzke miesto dochádza k stratám na smerovači pred týmto úzkym miestom. Tieto straty zaznamenávajú všetci odosielatelia. Keď každý z nich pri strate zníži rýchlosť odosielania na polovicu, nie je toto spomalenie u všetkých rovnaké – tí, ktorí odosielali rýchlejšie znížia rýchlosť viac ako tí, ktorí odosielali pomalšie (každý na polovicu svojej rýchlosti). Postupným opakovaním strát, nerovnakého spomalenia a následného rovnakého zvyšovania rýchlosti dochádza k vyrovnaniu prenosových rýchlostí.

Táto spravodlivosť sa týka iba TCP spojení a nemá nič spoločné s rovnakým využívaním prenosovej rýchlosti koncovými stanicami. Každá stanica môže mať naraz otvorený rôzny počet TCP spojení. Tá stanica, ktorá ich má otvorených viac, bude využívať väčšiu časť prenosového pásma. Na tomto princípe fungujú aj akcelerátory sťahovania napr. súborov z webových stránok. Jednoducho otvoria viac paralelných TCP spojení s rovnakým serverom a „ukradnú“ tak pre seba viac prenosovej rýchlosti na úkor ostatných TCP spojení prechádzajúcich cez spoločné úzke miesto.