Timery TCP

Rola RTO, estymacja RTT (Jacobson/Karels), keep-alive, persistence, oraz TIME-WAIT.

Wprowadzenie: Niewidzialny Zegar Komunikacji

Wyobraź sobie rozmowę z kimś przez SMS-y. Wysyłasz pytanie i oczekujesz odpowiedzi. Ale jak długo czekasz? Jeśli nie otrzymasz odpowiedzi, w którym momencie zakładasz, że wiadomość zaginęła i wysyłasz ją ponownie? A co, jeśli w rozmowie występują długie przerwy – skąd wiesz, czy druga osoba wciąż tam jest, czy może odłożyła telefon?

Ta prosta interakcja ludzka podkreśla fundamentalne wyzwanie w komunikacji: zarządzanie czasem i oczekiwaniami. Internet jest środowiskiem . Nie ma żadnych gwarancji, ile czasu zajmie pakietowi danych podróż od nadawcy do odbiorcy. Może to zająć kilka milisekund lub kilka sekund, a pakiet może zaginąć całkowicie.

Aby zarządzać tą głęboką niepewnością i spełnić obietnicę niezawodności, Protokół Kontroli Transmisji (TCP) polega na zaawansowanym systemie wewnętrznych stoperów, czyli liczników czasu (timerów). Te liczniki to nie tylko proste odliczanie; są to dynamiczne, adaptacyjne mechanizmy, które rządzą każdym aspektem cyklu życia połączenia. Decydują, kiedy przestać czekać na potwierdzenie, jak długo utrzymywać połączenie po jego zamknięciu, jak sondować niereagującego partnera i kiedy sprzątać stare, bezczynne połączenia. Liczniki czasu TCP to niewidzialne serce protokołu, stale podejmujące decyzje oparte na czasie, aby zapewnić płynny i niezawodny przepływ danych przez nieprzewidywalną przestrzeń internetu.

1. Licznik Czasu Retransmisji: Ostateczna Siatka Bezpieczeństwa TCP

Najważniejszym licznikiem w TCP jest Licznik Czasu Retransmisji, który zarządza Limitem Czasu Retransmisji (RTO). Jego głównym zadaniem jest zapobieganie sytuacji, w której połączenie utknie w martwym punkcie na zawsze, jeśli segment danych lub jego potwierdzenie zostanie utracone.

Wyzwanie RTO: Ustawienie Idealnego Alarmu

Wyzwanie dla TCP polega na ustawieniu odpowiedniej wartości limitu czasu. Internet jest dynamiczny; opóźnienie w połączeniu z Warszawy do pobliskiego serwera w Poznaniu jest znacznie inne niż do serwera w Sydney w Australii. Co więcej, warunki sieciowe mogą się zmieniać z chwili na chwilę z powodu przeciążenia.

  • Jeśli RTO jest ustawione zbyt krótko, TCP podda się zbyt wcześnie. Może retransmitować segment, który był po prostu opóźniony, a nie utracony. Prowadzi to do niepotrzebnych retransmisji, marnując przepustowość i dodatkowo przyczyniając się do przeciążenia sieci.
  • Jeśli RTO jest ustawione zbyt długo, TCP będzie czekać zbyt długo po autentycznej utracie pakietu. Prowadzi to do długich okresów bezczynności i powolnego odzyskiwania sprawności, poważnie wpływając na wydajność aplikacji.

Aby rozwiązać ten problem, TCP nie może używać stałej, statycznej wartości RTO. Zamiast tego musi dynamicznie obliczać RTO dla każdego połączenia, stale dostosowując je na podstawie obserwowanych warunków sieciowych. Jest to osiągane przez pomiar połączenia.

Algorytm Jacobsona/Karelsa: Szacowanie RTT i RTO

Klasyczną i wysoce skuteczną metodą obliczania RTO jest algorytm Jacobsona/Karelsa. Nie używa on tylko ostatniego pomiaru RTT; oblicza wygładzoną średnią i, co kluczowe, uwzględnia zmienność opóźnienia.

  1. Pomiar Próbki RTT ('SampleRTT')

    TCP ciągle mierzy czas potrzebny na otrzymanie ACK dla wysłanego segmentu. Zapisuje czas wysłania segmentu, a gdy dotrze odpowiednie ACK, oblicza upłynięty czas. To jest 'SampleRTT'. Problem pojawia się jednak przy retransmitowanych pakietach: jeśli dotrze ACK dla retransmitowanego segmentu, czy dotyczy ono oryginalnej transmisji, czy retransmisji? Ta niejednoznaczność może zaburzyć oszacowanie RTT. Aby to rozwiązać, Algorytm Karna dyktuje, że TCP nie powinno aktualizować swojego oszacowania RTT na podstawie pomiarów z retransmitowanych segmentów.

  2. Obliczanie Wygładzonego RTT ('SRTT')

    Pojedynczy 'SampleRTT' może być zakłócony. Aby uzyskać bardziej stabilne oszacowanie typowego RTT, TCP utrzymuje Wygładzony Czas Obiegu, 'SRTT'. Jest on obliczany jako ważona średnia ruchoma:

    SRTT=(1α)SRTTstary+αSampleRTTSRTT = (1 - \alpha) \cdot SRTT_{stary} + \alpha \cdot SampleRTT

    Parametr α\alpha (alfa) to współczynnik wygładzania, zazwyczaj 1/81/8. Oznacza to, że nowy 'SRTT' to 87,5% starej średniej plus 12,5% najnowszego pomiaru. Wygładza to sporadyczne skoki, jednocześnie pozwalając na dostosowanie oszacowania do zmieniających się warunków sieciowych.

  3. Uwzględnianie Zmienności ('RTTvar')

    Średnia to za mało. Połączenie może mieć średni RTT 100 ms, ale czasem wynosi on 80 ms, a czasem 120 ms. Aby być bezpiecznym, RTO musi uwzględniać tę zmienność. TCP oblicza również wariancję RTT, 'RTTvar', która szacuje, jak bardzo 'SampleRTT' zazwyczaj odbiega od średniego 'SRTT'.

    RTTvar=(1β)RTTvarstary+βSRTTSampleRTTRTTvar = (1 - \beta) \cdot RTTvar_{stary} + \beta \cdot |SRTT - SampleRTT|

    Parametr β\beta (beta) to kolejny współczynnik wygładzania, zazwyczaj 1/41/4. Oblicza on wygładzoną średnią bezwzględnej różnicy między próbkami a bieżącą średnią.

  4. Obliczanie Końcowego RTO

    Na koniec, Limit Czasu Retransmisji jest obliczany przez wzięcie wygładzonej średniej i dodanie marginesu bezpieczeństwa opartego na wariancji. Margines ten jest kluczowy, aby uniknąć przedwczesnych timeoutów.

    RTO=SRTT+4RTTvarRTO = SRTT + 4 \cdot RTTvar

    Użycie czterokrotności średniego odchylenia zapewnia solidny bufor, który radzi sobie z większością normalnych wahań w sieci bez niepotrzebnego wygaszania licznika.

  5. Wykładnicze Wycofywanie (Exponential Backoff)

    Co się stanie, jeśli retransmitowany segment również nie otrzyma ACK? Gdy licznik RTO wygaśnie, TCP zakłada, że coś jest poważnie nie tak z siecią. Nie tylko retransmituje segment, ale także stosuje wykładnicze wycofywanie. Podwaja wartość RTO za każdy kolejny timeout dla tego samego segmentu. Na przykład, jeśli początkowe RTO wynosiło 1 sekundę, następne wyniesie 2 sekundy, potem 4, 8 itd., aż do maksymalnej wartości. Zapobiega to zalewaniu mocno przeciążonej sieci powtarzającymi się retransmisjami.

2. Licznik Czasu TIME-WAIT (2MSL Timer): Sprzątanie Przeszłości

Po pomyślnym zamknięciu połączenia TCP przez czteroetapowe uzgadnianie, strona, która zainicjowała zamknięcie, nie przechodzi od razu do stanu 'CLOSED'. Zamiast tego wchodzi w specjalny stan kwarantanny zwany TIME-WAIT. Stan ten jest rządzony przez licznik czasu TIME-WAIT, który jest ustawiony na 2MSL.

to maksymalny czas, przez który segment może krążyć po internecie, zanim zostanie odrzucony. Oczekiwanie przez podwójny ten czas daje silną gwarancję, że połączenie jest definitywnie zakończone i nie będzie zakłócać przyszłych połączeń.

Dwa Cele Stanu TIME-WAIT

Ten okres oczekiwania może wydawać się nieefektywny, ale rozwiązuje dwa krytyczne potencjalne problemy.

  • Implementacja Niezawodnego Zakończenia Połączenia

    Ostatnim krokiem czteroetapowego uzgadniania jest wysłanie przez aktywnego zamykającego ostatniego ACK w odpowiedzi na FIN pasywnego zamykającego. A co jeśli to ostatnie ACK zaginie? Bez stanu TIME-WAIT aktywny zamykający natychmiast przeszedłby do stanu CLOSED. Pasywny zamykający jednak nigdy nie otrzymałby ACK. Jego licznik FIN by wygasł, i retransmitowałby swój FIN. Aktywny zamykający, będąc już w stanie CLOSED, otrzymałby ten retransmitowany FIN i, nie mając pamięci o starym połączeniu, prawdopodobnie odpowiedziałby pakietem RST (Reset). To nie jest eleganckie zakończenie.

    Czekając w stanie TIME-WAIT, aktywny zamykający utrzymuje stan połączenia przez 2MSL. Jeśli ostatnie ACK zaginęło i dotrze retransmitowany FIN, aktywny zamykający może po prostu ponownie wysłać ostatnie ACK, pozwalając pasywnemu zamykającemu elegancko zakończyć połączenie zgodnie z przeznaczeniem.

  • Zapobieganie Uszkodzeniu Nowych Połączeń przez Opóźnione Duplikaty

    Internet może czasem poważnie opóźniać pakiety. Teoretycznie możliwe jest, że zduplikowany pakiet z poprzedniego połączenia utknie w routerze na długi czas, a następnie zostanie dostarczony znacznie później.

    Wyobraźmy sobie, że połączenie między 1.2.3.4:500001.2.3.4:50000 a 5.6.7.8:805.6.7.8:80 zostaje zamknięte. Kilka sekund później użytkownik otwiera nową kartę w przeglądarce, a system operacyjny przypadkowo ponownie używa tego samego portu, tworząc nowe połączenie między 1.2.3.4:500001.2.3.4:50000 a 5.6.7.8:805.6.7.8:80. Gdyby teraz dotarł opóźniony pakiet danych ze starego połączenia, nowe połączenie, mające tę samą parę gniazd, mogłoby go błędnie zaakceptować jako prawidłowe dane, prowadząc do uszkodzenia danych.

    Oczekiwanie przez 2MSL zapewnia, że do czasu przejścia połączenia do stanu CLOSED i udostępnienia portu do ponownego użycia, wszystkie pakiety z poprzedniej inkarnacji tego połączenia na pewno wygasły i zostały odrzucone z sieci.

3. Licznik Czasu Podtrzymania (Persistence Timer): Przezwyciężanie Impasu Okna Zerowego

Mechanizm kontroli przepływu w TCP, chociaż skuteczny, ma potencjalny scenariusz awarii. Odbiorca, którego bufor jest pełny, ogłosi okno odbiorcze równe zero ('rwnd=0'), informując nadawcę, aby przestał wysyłać. Nadawca stosuje się do tego. Później aplikacja odbiorcy odczytuje część danych, zwalniając miejsce w buforze. Odbiorca wysyła wtedy segment TCP z potwierdzeniem ACK i nową, niezerową aktualizacją okna, informując nadawcę, że może wznowić pracę.

Problem polega na tym, że TCP nie potwierdza segmentów zawierających tylko ACK. Jeśli ten kluczowy segment aktualizacji okna, który nie przenosi danych, zostanie utracony w sieci, nadawca nigdy się nie dowie, że odbiorca jest ponownie gotowy. Nadawca czeka na aktualizację okna, a odbiorca czeka na więcej danych. Obie strony czekałyby wiecznie, co prowadziłoby do impasu.

Licznik Czasu Podtrzymania (ang. Persistence Timer) został zaprojektowany specjalnie po to, by przełamać ten impas. Kiedy nadawca otrzyma ogłoszenie o oknie zerowym, uruchamia licznik podtrzymania. Jeśli ten licznik wygaśnie, zanim dotrze aktualizacja okna, nadawca przesyła mały pakiet zwany sondą okna (ang. window probe). Ta sonda zmusza odbiorcę do odpowiedzi z potwierdzeniem ACK zawierającym jego aktualny rozmiar okna.

  • Jeśli okno nadal wynosi zero, nadawca resetuje licznik podtrzymania, często stosując strategię wykładniczego wycofywania, i próbuje ponownie później.
  • Jeśli okno jest teraz niezerowe, impas jest przełamany, a transmisja danych może zostać wznowiona.

4. Licznik Czasu Podtrzymania Aktywności (Keep-Alive Timer): Sprawdzanie Pulsu

Połączenia TCP mogą czasem pozostawać bezczynne przez bardzo długi czas. Rozważmy sesję SSH do zdalnego serwera; można ją pozostawić otwartą na wiele godzin bez wpisywania czegokolwiek. Problem pojawia się, gdy jedna ze stron ulegnie awarii lub zostanie odłączona od sieci (np. klient straci połączenie Wi-Fi) bez możliwości wysłania pakietu FIN. Druga strona, zazwyczaj serwer, zostałaby z połączeniem półotwartym, utrzymując zasoby systemowe (pamięć, gniazda) dla połączenia, które już nigdy nie będzie używane. Jeśli zgromadzi się wystarczająco dużo takich połączeń, może to prowadzić do wyczerpania zasobów.

Licznik Czasu Keep-Alive to opcjonalny mechanizm TCP do wykrywania tych martwych połączeń. Jeśli połączenie było bezczynne przez długi czas (zazwyczaj dwie godziny), licznik keep-alive na serwerze wygaśnie.

  1. Serwer wysyła segment sondy keep-alive. Jest to segment ACK z celowo nieprawidłowym numerem sekwencyjnym, zaprojektowany tak, aby wywołać odpowiedź.
  2. Jeśli klient wciąż żyje i ma się dobrze, odpowie ACK wskazującym swój bieżący oczekiwany numer sekwencyjny. Po otrzymaniu tej odpowiedzi serwer wie, że połączenie jest nadal ważne i resetuje licznik keep-alive na kolejne dwie godziny.
  3. Jeśli klient uległ awarii i został ponownie uruchomiony, nie rozpozna połączenia i odpowie pakietem RST (Reset), co posprząta połączenie półotwarte.
  4. Jeśli klient jest po prostu nieosiągalny, w ogóle nie odpowie. Serwer wyśle kilka kolejnych sond w krótszych odstępach czasu. Jeśli żadna nie otrzyma odpowiedzi, serwer dochodzi do wniosku, że połączenie jest martwe i je zamyka, zwalniając zasoby.

Chociaż mechanizm ten jest przydatny, bywa kontrowersyjny, ponieważ domyślne dwie godziny mogą być zbyt długie, a pakiety keep-alive mogą powodować problemy z zaporami sieciowymi lub tymczasowymi przerwami w działaniu sieci. Wiele aplikacji woli implementować własną logikę pulsu (heartbeat) lub keep-alive na warstwie aplikacji.

    Timery TCP | Teleinf Edu