Przed przeniesieniem pierwszego bajtu danych
TCP jest protokołem orientowanym w połączeniach, zgrubnie na IP, który nie jest — IP prosto przekierowuje pojedyncze pakety ku adresowi bez pamięci o tym, co przedtem ani co nastąpiło. TCP musi budować iluzję wiarygodnej, uporządkowanej, połączonych strumieni na podstawie tego zapomnianego substratu, i zaczyna od tego, że obie końce zgadzają się na miejsce rozpoczęcia strumienia. Ta zgoda to trzykrotna dżentonka, a ta symulacja animuje ją pakiet po pakiecie przed przejściem do części, której większość wyjaśnień pomija: co się dzieje z przechodnictwem połączenia, gdy rzeczywiście przepływa dane i sieć napotkuje zagęszczenie.
SYN, SYN-ACK, ACK
Każdy bokTCP połączenia śledzi swój własny numer szeregowy — licznik identyfikujący przesunięcie bajtowe strumienia, który chce wysłać — a całe podeszwowanie jest skonstruowane do wymiany i potwierdzenia tych wartości początkowych:
klient -> serwer: SYN, seq = x (Zamieram rozpocząć od bajtu x) serwer -> klient: SYN-ACK, seq = y, ack = x + 1 (Zamieram rozpocząć od y; potwierdzam twoje x) klient -> serwer: ACK, ack = y + 1 (Potwierdzam twoje y) Po tej wymianie obydwa boki wiedzą o numerze szeregowym początkowym drugiej strony i potwierdzili go, co pozwala na śledzenie każdego kolejnego segmentu danych, umieszczanie ich w odpowiedniej kolejności oraz potwierdzenie otrzymania nawet jeśli protokół IP może dostarczać podstawowe paczki nieporządanymi lub całkowicie usunąć niektóre z nich. Podeszwowanie to również negocjuje parametry połączenia — wielkość okna, maksymalny rozmiar segmentu, wsparcie dla potwierdzeń wybiorowych — załączane na tym samym trzech pakietach, dlatego powolne lub utraty podeszwowanie opóźnia nie tylko rozpoczęcie połączenia, ale także negocjowane cechy, które kształtują wszystko co nastąpi potem.
client -> server: SYN, seq = x (I plan to start at byte x)
server -> client: SYN-ACK, seq = y, ack = x + 1 (I plan to start at y;
I confirm your x)
client -> server: ACK, ack = y + 1 (I confirm your y)
Po nawiązaniu połączenia: okno zagęszczania
LIVE połączenie TCP nie może przesyłać danych tak szybko, jak to pozwala link nadawcy — gdzieś na trasie może być wolniejszy link lub zatłoczony router, a wysyłanie szybciej niż sieć jest w stanie założyć tylko wypełnia bufora aż do przepełnienia i utraty pakietów. Odpowiedzią TCP jest okno zagęszczania (cwnd): ograniczenie nadawcy na ilość niepotwierdzonych bajtów, które mogą być w trakcie przesyłania, dostosowywane ciągle na podstawie tego, czy pakety są poprawnie otrzymane lub utracone — to jest AIMD (additive-increase multiplicative-decrease), a szpiccetka, którą tworzy, to druga część demonstracji w czasie rzeczywistym.
Slow start: wykładniczy wzrost, mimo nazwy
Niedługo po przeprowadzeniu ręcznie wypukłej (handshake), TCP nie ma żadnych informacji na temat pojemności ścieżki, więc zaczyna konserwatywnie — ale szybko rośnie. Slow start podwójnia cwnd około co RTT, ponieważ każda potwierdzona sekcja zwiększa cwnd o wartość jednej sekcji, a w ciągu jednego RTT cała okno zawiera się w potwierdzonych sekcjach:
slow start (podczas gdy cwnd cwnd około podwójny co RTT // to jest wzrost wykładniczy mimo nazwy „slow start” - // jest „spowolniony” tylko w porównaniu do wysłania całego okna od razu Ten proces kontynuuje się, dopóki cwnd nie osiągnie próg (ssthresh) — zazwyczaj ustawionego na podstawie poprzedniego wydarzenia straty — lub aż do momentu wykrycia strony, w którym TCP przełącza tryby.
slow start (while cwnd < ssthresh): on each ACK received: cwnd += 1 segment // an entire cwnd of segments ACKed within ~1 RTT => cwnd roughly doubles per RTT // this is exponential growth despite the name "slow start" - // it's "slow" only relative to sending the whole window at once
Unikanie zagęszczenia: zwiększanie dodawanej w sposób addytywny
Po przekroczeniu ssthresh TCP przyjmuje założenie, że jest blisko rzeczywistej pojemności ścieżki i przełącza się na ostrożne liniowe zwiększanie — zwiększając cwnd o około jedno segmenty na czas pełnego przepływu wiadomości zamiast dla każdego potwierdzenia otrzymania (ACK), dwaustoskawo sprawdzając, czy istnieje jeszcze jakieś wolne miejsce:
congestion avoidance (cwnd >= ssthresh): on each full window of ACKs (~1 RTT): cwnd += 1 segment // linear growth - the "additive increase" half of AIMD
Strata: zmniejszenie mnożone
Pakiet zakończony strażą (wykryta przez either retransmisji czasu wyjścia, lub bardziej obwączona przez powtórzone potwierdzenia oznaczające przestój w otrzymanym strumieniu) jest niejawnym sygnałem TCP, że przekroczył rzeczywistą pojemność sieci — brak jasnej notyfikacji zatargu, TCP wnioskuje o zatarg na podstawie strony. Reakcja to agresywne zmniejszenie mnożone, a nie łagodna korekcja:
w momencie wykrycia straty: ssthresh = cwnd / 2 cwnd = ssthresh (lub cwnd = 1 segment, dla czasu wyjścia - pełny restart z powrotem do startu wolnego) -> wznowienie w fazie unikania zatargu od zmniejszonego o połowe wartości Asymetrię — zwiększanie dodawane, zmniejszanie mnożone — jest świadomie zaplanowana i to co sprawia, że AIMD konverguje równomiernie i stabilnie nawet wtedy, gdy wiele niezależnych połączeń TCP dzieli jedno ograniczone łącze bez koordynacji między nimi: liniowe zwiększanie jest ostrożne dość, aby nie przekroczyć pojemności o znaczną ilość przed kolejnym sygnałem strony, a zmniejszanie mnożone w momencie straty jest szybkie do wystarczającego odstępu, aby pusty kolejnik zatargowany pusta szybko, zamiast ponownie przekraczać go. Powtarzający się cykl wzrostu liniowego, a następnie zmniejszenia mnożone w momencie straty tworzy charakterystyczną grafikę przebiegu przepustowości w kształcie szpikulca, która jest drugą połową tego symulacji live plot, a to jest jedna z najważniejszych części projektowania algorytmu na całym internetie — to powoduje, że zatargowane łącze degraduje się łagodnie w delikatne, sprawiedliwe przepustowość dołączanej, zamiast klesnąć w zatarg.
on loss detected:
ssthresh = cwnd / 2
cwnd = ssthresh (or cwnd = 1 segment, for a timeout -
a full restart back to slow start)
-> resume in congestion avoidance from the halved value
Często zadawane pytania
Dlaczego TCP potrzebuje trzech pakietów do zainicjowania połączenia zamiast jednego?
Bo obie strony muszą uzyskać zgody na numer sekwencyjny, a każda strona musi mieć dowód, że druga strona rzeczywiście otrzymała swój numer, a nie tylko go wysłała. SYN przedstawia numer sekwencyjny klienta, SYN-ACK przedstawia numer sekwencyjny serwera i potwierdza numer klienta, a końcowy ACK potwierdza numer serwera — po trzech pakietach obie strony mają wspólnie potwierdzony, nie tylko wspólnie stwierdzony punkt początkowy.
Jeśli start szybki rosnie w wykładniczy sposób, dlaczego jest nazywany 'spowolnionym'?
Jest spowolniony tylko w stosunku do alternatywy wysyłania pełnej dużej liczby pakietów natychmiast, bez żadnego wzrostu, co wcześniejsze, bardziej prostokrotne implementacje TCP robily i które wiarygodnie zaskakiwały intermedialne routerzy. W porównaniu z tym, od jednego segmentu i podwójne się w każdym odcinku powrotnej trasji jest konserwatywnym wyborem — nawet jeśli w absolutnych wyższych wymiarach wzrost jest wykładniczy.
Dlaczego TCP zmniejsza szybkość wysyłania pół na stratę pakietu zamiast mniejszej korekcji?
Bo TCP nie ma jasnej sygnału dla tego, jak zaopatrzony jest sieć — stracony pakiet to jedyna odpowiedzialność, którą otrzymuje. Po detekcji straty sieć może już być znacznie zaopatrzona. Duża i szybka zmiana (zmniejszenie mnożone) wyciśnia szybko koleję zablokowaną; potem wolne, liniowe wzrost (zwiększanie dodawane) jest to, co sprawia, że algorytm konverguje do stabilnej, równo podzielonej części przepustowości bez powtarzających się nadmiarów i ponownego spowodowania straty.
Wypróbuj na żywo
Wszystko powyżej działa bezpośrednio w Twojej przeglądarce — otwórz TCP/IP Handshake & Congestion Control — Slow Start, AIMD i zmieniaj parametry podczas działania. Nic nie jest instalowane ani przesyłane na serwer, cały model działa w jednej karcie.
▶ Otwórz symulację TCP/IP Handshake & Congestion Control — Slow Start, AIMD