TCP/IP podział pakietu na ramki

0

Cześć,
miałem kiedyś problem, że pakiety wysyłane z serwera przychodziły do klienta podzielone. Rozwiązałem to tworząc bufor i składając te pakiety przed dalszą obróbką np
w jednym pakiecie przychodziło

<pole1|pole2|pol

a w drugim

e3|pole4>

ostatecznie potrzebowałem

<pole1|pole2|pole3|pole4>

i do takiej postaci składałem bez problemu.

Teraz czytam sobie o tym trochę więcej i dowiedziałem się, że w przypadku kiedy pakiet ma większy rozmiar może być podzielony na ramki. Ale! Można dodać flagę DF dzięki czemu pakiet nie będzie dzielony:

Flagi (3 bity) - (ang. Flag) flagi wykorzystywane podczas fragmentacji datagramów. Zawierają dwa używane pola: DF, które wskazuje, czy pakiet może być fragmentowany oraz MF, które wskazuje, czy za danym datagramem znajdują się kolejne fragmenty.

Czy faktycznie ustawienie takiej flagi spowoduje pewnego rodzaju wymuszenie przetransportowania pakietu w całości bez jego dzielenia?

2

TCP jest protokołem strumieniowym opartym o bajty (https://stackoverflow.com/questions/3017633/difference-between-message-oriented-protocols-and-stream-oriented-protocols) i tak właśnie powinieneś go traktować - tzn. nie zakładaj, że jakaś flaga sprawi, że w każdej sytuacji jedna wiadomość zostanie wysłana w jednym pakiecie.

Temat staje trochę bardziej skomplikowany, jeśli dodatkowo weźmiesz pod uwagę takie rzeczy jak algorytm Nagle'a ;-)

4

TCP/IP to model i stos sieciowy, który zawiera protokoły transportowe TCP i UDP. Dlatego, żeby przejść do dalszej dyskusji może kolega wyjaśni jakiego protokołu warstwy transportowej używał. Obstawiam TCP, bo z uwagi na strumieniowy charakter mogło dojść do segmentacji i potem fragmentacji, ale dla uściślenia niech autor potwierdzi.

Jeżeli chcesz wysłać rekordy po protokole strumieniowym, to powinieneś zastosować serializację do formatu, z którego da się potem odtworzyć oryginalne dane. Niskopoziomowo to może być kodowanie TLV, wysokopoziomowo np. JSON. Chodzi o to, żeby podczas deserializacji strumienia bajtów po stronie odbiorcy dało się stwierdzić gdzie kończy się wiadomość/rekord.

To jak pakiet zostanie podzielony, zależy od implementacji stosu sieciowego. Używając funkcji write/send przekazujesz bajty do stosu sieciowego. W przypadku TCP dane nie muszą być od razu wysyłane, mogą być buforowane po stronie stosu sieciowego w jądrze systemu, połączone i podzielone, dopiero po chwili wysłane. Wynika to ze strumieniowego charakteru protokołu TCP.

  • a) Twoja oryginalna wiadomość, jeżeli okaże się zbyt duża, może zostać rozbita na wiele segmentów TCP, każdy zapakowany we własną ramkę IP.
  • b) Stos sieciowy może też zadecydować, by zostawić jeden duży pakiet, ale podzielić go na wiele fragmentów. Dzieje się tak, gdy rozmiar segmentu TCP przekracza wartość MSS.
  • c) Może zajść jednocześnie a i c.

Generalnie fragmentacja nie jest pożądana, a już szczególnie zrobiona przez urządzenia pośredniczące, gdyż obniża wydajność. Dlatego powstały takie mechanizmy jak path MTU discovery, by od razu podzielić pakiet jak najlepiej się da.

Jeżeli zastosujesz opcję DF (dont't fragment), to licz się z tym, że w zależności jakiego protokołu warstwy transportowej używasz, to wiadomość w ogóle może nie dojść. Jeżeli jakieś urządzenie na ścieżce będzie miało mniejsze MTU, to pakiet zostanie odrzucony . Z RFC 791:

An internet datagram can be marked "don't fragment." Any internet datagram so marked is not to be internet fragmented under any circumstances. If internet datagram marked don't fragment cannot be delivered to its destination without fragmenting it, it is to be discarded instead.

Efekt dla protokołu TCP, jak się okazuje, jest odwrotny niż byś się spodziewał właśnie z uwagi na path MTU discovery. Wiadomość zostanie odrzucona przez urządzenie sieciowe gdzieś na ścieżce, nadawca zostanie o tym poinformowany przez protokół ICMP i obniży wielkość segmentu TCP (dzieląc Twoją wiadomość ...) oraz wielkość pakietu IP, aby zmieścił się w MTU urządzenia, które pakiet IP odrzuciło. Opcja don't fragment nie jest przeznaczona dla wiadomości warstwy aplikacji.

0

Dzięki za szczegółową odpowiedź. Oczywiście chodzi o TCP.

Jeżeli chcesz wysłać rekordy po protokołu strumieniowym, to powinieneś zastosować serializację do formatu, z którego da się je potem odtworzyć oryginalne dane. Niskopoziomowo to może być kodowanie TLV, wysokopoziomowo np. JSON. Chodzi o to, żeby podczas deserializacji strumienia bajtów po stronie odbiorcy dało się stwierdzić gdzie kończy się wiadomość/rekord.

Czyli wychodzi na to, że jak bym nie kombinował to i tak muszę problem rozwiązać tak jak opisałem to w pierwszym poście. Czyli bufor i składanie pakietów. Dobrze zrozumiałem?

0
karpov napisał(a):

Dzięki za szczegółową odpowiedź. Oczywiście chodzi o TCP.

Jeżeli chcesz wysłać rekordy po protokołu strumieniowym, to powinieneś zastosować serializację do formatu, z którego da się je potem odtworzyć oryginalne dane. Niskopoziomowo to może być kodowanie TLV, wysokopoziomowo np. JSON. Chodzi o to, żeby podczas deserializacji strumienia bajtów po stronie odbiorcy dało się stwierdzić gdzie kończy się wiadomość/rekord.

Czyli wychodzi na to, że jak bym nie kombinował to i tak muszę problem rozwiązać tak jak opisałem to w pierwszym poście. Czyli bufor i składanie pakietów. Dobrze zrozumiałem?

W przypadku TCP - tak.
UDP z kolei gwarantuje, że jeden write to jeden segment (może być wiele ramek ip).

Dodatkowo nie wiem czy zdajesz sobie sprawę, ale podzielenie Twoich danych to nie wina fragmentacji na warstwie sieciowej, gdyż ramki IP dla pojedyńczego pakietu są składane zanim zostaną przekazane wyżej, zarówno w przypadku protokołu TCP jak i UDP. Ergo, to skutek segmentacji pakietów po stronie nadawcy albo urządzenia, które działało w warstwie 4 (np. NAT albo firewall, który robił inspekcję pakietów TCP albo wiadomości warstwy aplikacji jak HTTP. Lub właśnie braku path MTU discovery, co poskutkowało składaniem ramek IP, a następnie ponownym ich dzieleniem przez urządzenie sieciowe, by zmieścić się w MTU sieci wychodzącej).

0
nalik napisał(a):

Dodatkowo nie wiem czy zdajesz sobie sprawę, ale podzielenie Twoich danych to nie wina fragmentacji na warstwie sieciowej, gdyż ramki IP dla pojedyńczego pakietu są składane zanim zostaną przekazane wyżej, zarówno w przypadku protokołu TCP jak i UDP. Ergo, to skutek segmentacji pakietów po stronie nadawcy albo urządzenia, które działało w warstwie 4 (np. NAT albo firewall, który robił inspekcję pakietów TCP albo wiadomości warstwy aplikacji jak HTTP. Lub właśnie braku path MTU discovery, co poskutkowało składaniem ramek IP, a następnie ponownym ich dzieleniem przez urządzenie sieciowe, by zmieścić się w MTU sieci wychodzącej).

Zaczęło to do mnie docierać kiedy czytałem Twoje wpisy ;)

Pewnie bufor i tak zostawię dla bezpieczeństwa ale zadam pytanie teoretyczne (jestem ignorantem i totalnie się na tym nie znam więc wybacz jeżeli czegoś nie załapałem) - czy jest więc jakaś szansa żeby zapobiegać takiemu dzieleniu na wyższych warstwach?

0
karpov napisał(a):

czy jest więc jakaś szansa żeby zapobiegać takiemu dzieleniu na wyższych warstwach?

Szczerze, to nie wiem, chyba nie, gdyż nie masz wpływu na urządzenia pośredniczące. Nie ma sensu temu zapobiegać, to nie bug, tak ma działać.

0

Wielkie dzięki, sporo mi rozjaśniłeś.

1 użytkowników online, w tym zalogowanych: 0, gości: 1