1. Warstwa Transportowa

1.3. Sesje i połączenia

W warstwie transportowej sesja oznacza logiczne, dwukierunkowe połączenie pomiędzy dwoma procesami (aplikacjami) działającymi na odległych hostach. Choć model OSI wyodrębnia warstwę sesji jako oddzielny poziom, w praktyce funkcje tej warstwy często są realizowane przez transport, zwłaszcza przez protokół TCP. Sesja obejmuje nie tylko samą transmisję danych, ale również procesy zestawiania, utrzymywania i kończenia połączenia, a także zarządzanie numeracją bajtów, potwierdzeniami, retransmisjami i zamykaniem komunikacji.

W TCP sesja rozpoczyna się od nawiązania połączenia, które realizowane jest poprzez mechanizm trójfazowego uzgadniania (three-way handshake). Proces ten służy do zsynchronizowania obu stron komunikacji, ustalenia początkowych numerów sekwencyjnych oraz potwierdzenia gotowości do rozpoczęcia transmisji danych. Inicjatorem połączenia jest zazwyczaj klient, który wysyła pierwszy segment z ustawioną flagą SYN i losowo wybranym numerem sekwencyjnym.

Pierwszy segment z SYN ustala początkowy numer sekwencyjny klienta. Drugi segment z SYN i ACK od serwera potwierdza numer klienta oraz podaje własny numer sekwencyjny. Trzeci segment z samym ACK potwierdza numer serwera. Po wykonaniu tej wymiany uznaje się, że połączenie zostało ustanowione i możliwe jest przesyłanie danych w obu kierunkach. Co ważne, każde połączenie TCP jest pełnodupleksowe, co oznacza, że każda ze stron może niezależnie wysyłać i odbierać dane.

Po ustanowieniu połączenia TCP utrzymuje stan sesji przez cały czas trwania komunikacji. W tym celu każdy z końców przechowuje informacje o bieżącym numerze sekwencyjnym, numerze potwierdzenia, rozmiarze okna odbiorczego, a także statusie flag i czasie życia połączenia. Dzięki temu możliwe jest śledzenie, które dane zostały wysłane, które zostały potwierdzone, oraz które muszą być retransmitowane. Stan ten jest przechowywany w strukturach nazywanych kontrolnymi blokami połączeń (Transmission Control Blocks, TCB), które zawierają również informacje o lokalnym i zdalnym porcie oraz adresach IP.

Zakończenie sesji TCP odbywa się w sposób kontrolowany i również składa się z wymiany segmentów. W przeciwieństwie do nawiązywania połączenia, które odbywa się w trzech krokach, zamykanie sesji TCP wymaga czterech etapów – każdy kierunek transmisji musi zostać zamknięty niezależnie.

Każda strona, która nie ma już danych do wysłania, inicjuje zamknięcie połączenia przez przesłanie segmentu z flagą FIN. Druga strona potwierdza ten segment (ACK), ale dopóki sama ma dane do wysłania, utrzymuje połączenie otwarte. Dopiero po zakończeniu własnej transmisji również wysyła FIN, na co pierwsza strona odpowiada ACK. Ten proces zapewnia, że żadna ze stron nie utraci ostatnich danych. Po wysłaniu ACK w odpowiedzi na FIN, gniazdo TCP wchodzi w stan TIME_WAIT, w którym pozostaje przez 2×MSL (Maximum Segment Lifetime), aby zapobiec ponownemu wykorzystaniu tych samych numerów sekwencyjnych.

TCP wykorzystuje zestaw flag w nagłówku segmentu, które kontrolują przebieg sesji. Flaga SYN służy do inicjalizacji połączenia. Flaga ACK oznacza potwierdzenie odbioru segmentu lub numeru sekwencyjnego. Flaga FIN sygnalizuje zakończenie transmisji w jednym kierunku. Flaga RST używana jest do natychmiastowego zerwania połączenia – zazwyczaj w sytuacjach wyjątkowych, takich jak próba komunikacji z nieistniejącym gniazdem. W kontekście sesji flagi te odgrywają kluczową rolę w utrzymaniu spójnego stanu po obu stronach komunikacji.

Numeracja sekwencyjna to podstawowy mechanizm umożliwiający TCP śledzenie kolejności bajtów w strumieniu danych. Każdy bajt w strumieniu ma przypisany unikalny numer, a segmenty TCP zawierają numer początkowy bajtu, który zawierają. Odbiorca na podstawie numerów sekwencyjnych i potwierdzeń (ACK) wie, które bajty zostały poprawnie dostarczone, a nadawca – które mogą wymagać retransmisji. Numer sekwencyjny ustalany jest losowo na początku sesji (w ramach handshake) w celu zwiększenia bezpieczeństwa i ochrony przed atakami typu spoofing. Zarządzanie numerami sekwencyjnymi jest kluczowe, ponieważ pozwala na niezależne kontrolowanie dwóch jednokierunkowych strumieni danych w ramach jednego połączenia TCP.

Ważnym aspektem transmisji danych w TCP jest problem idempotencji, czyli odporności na wielokrotne wykonanie tej samej operacji. TCP nie zakłada, że przesłane dane dotrą tylko raz – każdy segment może zostać zduplikowany i retransmitowany. Aby zapewnić poprawność działania, protokoły wyższych warstw (np. HTTP, FTP) muszą być projektowane tak, aby potrafiły zidentyfikować i właściwie obsłużyć powtórne dostarczenie tych samych danych. Przykładowo, serwer HTTP powinien wiedzieć, czy klient rzeczywiście ponownie żąda pliku, czy tylko otrzymał powtórkę pakietu. TCP dba jedynie o to, aby wszystkie bajty zostały dostarczone w odpowiedniej kolejności i bez błędów – nie interpretuje ich znaczenia.

Połączenia TCP mogą czasem znajdować się w stanie tzw. half-open, czyli półotwartym. Taka sytuacja występuje wtedy, gdy jedna ze stron (np. klient) uważa, że połączenie jest nadal aktywne, podczas gdy druga strona (np. serwer) już je zakończyła lub uległa awarii. Półotwarte połączenia mogą prowadzić do wycieków zasobów i błędów komunikacyjnych. W celu wykrycia takich sytuacji TCP stosuje mechanizmy takie jak keep-alive, które co pewien czas wysyłają specjalne segmenty sprawdzające, czy druga strona nadal odpowiada. Jeśli nie, połączenie może zostać jednostronnie zamknięte.

Alternatywą dla TCP jest protokół UDP, który z definicji nie obsługuje sesji ani nie utrzymuje żadnego stanu. UDP wysyła datagramy niezależnie od siebie, bez uprzedniego nawiązywania połączenia, bez potwierdzeń i bez mechanizmów kontroli transmisji. Nie ma numeracji sekwencyjnej, nie ma retransmisji, nie ma kontroli przepływu ani zarządzania zatorami. Aplikacja korzystająca z UDP musi samodzielnie radzić sobie z problemami związanymi z kolejnością, duplikacją i utratą danych – jeśli w ogóle tego wymaga. Sesje, w rozumieniu TCP, nie istnieją w UDP. Każdy pakiet jest traktowany jako osobna jednostka logiczna, a nadawca nie ma informacji, czy odbiorca istnieje, czy odebrał dane, czy jest gotowy do dalszej komunikacji.

Brak sesji w UDP powoduje, że protokół ten jest bezstanowy, co oznacza, że nie przechowuje żadnych informacji o przeszłej komunikacji. Dzięki temu UDP charakteryzuje się bardzo niskim narzutem protokołu i jest idealny do transmisji w czasie rzeczywistym (np. VoIP, wideo, gry online), gdzie opóźnienia są bardziej krytyczne niż gwarancja dostarczenia. Niemniej jednak brak mechanizmów sesyjnych oznacza również brak ochrony przed wieloma typami błędów, które TCP obsługuje domyślnie.

Podsumowując, sesje w warstwie transportowej TCP są fundamentalnym elementem niezawodnej komunikacji w sieciach komputerowych. Obejmują one cały cykl życia połączenia: od negocjacji parametrów, przez zarządzanie stanem i numeracją, aż po eleganckie zakończenie. TCP oferuje pełen zestaw narzędzi do kontrolowania przepływu danych, ich kolejności, niezawodności oraz wykrywania problemów, podczas gdy UDP celowo rezygnuje z tych funkcjonalności na rzecz prostoty i szybkości. Zrozumienie różnic pomiędzy tymi podejściami jest kluczowe dla inżynierów projektujących aplikacje sieciowe, którzy muszą wybrać odpowiedni protokół w zależności od wymagań transmisji, niezawodności, opóźnień i obciążenia sieci.