Czy mogę bezpiecznie ignorować kolejność bajtów w sieci?


24

Tworzę aplikację serwer-klient, w której klient będzie działał w systemie Windows, a serwer prawdopodobnie w systemie Linux. Może później przeniosę klienta na komputery Mac i Linux, ale jeszcze nie teraz.

Wszystkie komputery domowe działają obecnie na little-endian. Przez pewien czas korzystałem z Google, ale tak naprawdę nie mogłem znaleźć listy urządzeń, które działają na big-endian. O ile mi wiadomo, niektóre układy Motorola nadal używają big-endian i może niektórych telefonów (nie planuję przenosić aplikacji na smartfony, więc nie ma to dla mnie znaczenia). Dlaczego więc miałbym zmieniać kolejność bajtów każdej liczby całkowitej, każdej krótkiej, każdej liczby zmiennoprzecinkowej, podwójnej itd. Do czytania i pisania , skoro już wiem, że zarówno serwer, jak i klient działają na little-endian?

To po prostu niepotrzebna praca. Więc moje pytanie brzmi: czy mogę bezpiecznie zignorować endianness i po prostu wysłać dane little-endian? Jakie są wady?


4
Skąd maszyny będą wiedzieć, czy otrzymują dane little-endian zamiast zwykłych / standardowych danych big-endian?
Ixrec

2
Musisz rozróżnić metadane wymagane przez protokół sieciowy od ładunku, który jest tylko garstką niezinterpretowanych bajtów dla wszystkich oprócz twojego kodu. Mam nadzieję, że nie rozwijasz własnego stosu sieciowego. W związku z tym zakładam, że pytanie dotyczy tylko ładunku, prawda?

2
@ delnan tak, tylko mówię o ładunku. Oczywiście nadal będę rozmawiać w sieciowej kolejności bajtów z samym stosem sieci.
tkausl

3
Pomyśl tylko z boku: czy naprawdę musisz pracować na poziomie abstrakcji, w którym endianizm jest problemem? Warto rozważyć użycie protokołów, dla których istnieją odpowiednie biblioteki, które zawierają cały ten „bałagan” niskiego poziomu. Następnie masz dodatkową premię, że dodawanie kolejnych klientów jest znacznie łatwiejsze.
godfatherofpolka

1
@tkausl Jeszcze tylko dwie przemyślenia: Zasadniczo IO jest bardzo wolne w porównaniu do obliczeń, więc wszelkie koszty ogólne związane z pracą na wyższym poziomie abstrakcji są najprawdopodobniej nieistotne. Może się nawet zdarzyć, że niektóre biblioteki osiągną lepsze wyniki niż w przypadku ręcznych implementacji ze względu na sprytne tworzenie pul zasobów i asynchroniczną obsługę itp. Tak więc najpierw dokładnie oceniłbym istniejące rozwiązania. Co więcej, biorąc pod uwagę Twój opis, poświęcę trochę uwagi na skalowalność, a nie wydajność, tutaj możesz ponownie skorzystać z protokołów wyższego poziomu.
godfatherofpolka

Odpowiedzi:


29

... dlaczego miałbym zmieniać kolejność bajtów ... skoro już wiem, że zarówno serwer, jak i klient działają na małym endianie? To po prostu niepotrzebna praca.

Nie jest to konieczne, jeśli możesz zagwarantować, że Twój kod będzie zawsze działał na architekturze little-endian. Jeśli zamierzasz mieć długą żywotność, warto podjąć dodatkowy wysiłek, aby uniknąć zakłócania sprawdzonego kodu za dekadę, gdy jakaś architektura big-endian stała się „wcielona” i okazuje się, że jest to dobry rynek dla Twoje zgłoszenie.

Istnieje standardowe bajtowe porządkowanie sieci. Jest to big-endian, ale nic nie mówi, że musisz go przestrzegać przy projektowaniu protokołu. Jeśli wiesz z wyprzedzeniem, że większość systemów, w których działa Twój kod, będzie mało wydajna, a wydajność ma krytyczne znaczenie, zadeklaruj, że „standardowe porządkowanie bajtów tkausl” i idź z nim. Tam, gdzie normalnie dzwonisz, htons()aby uporządkować potrzebne rzeczy, napisz makro o nazwie htots()warunkowo, które kompiluje się do niczego na architekturach little-endian i dokonuje ponownej aranżacji na big-endian.

Utrzymanie kodu do konwersji przychodzących i wychodzących nie jest tak naprawdę dużym wysiłkiem. Jeśli masz bardzo dużą liczbę wiadomości, znajdź sposób na ich wyrażenie i napisanie programu do generowania konwersji przychodzących i wychodzących.


10
Sformułowanie when designing your protocoljest ważne, ponieważ domyślnie mówi również, że ta opcja istnieje tylko przy projektowaniu nowego protokołu, a nie przy implementacji niektórych istniejących protokołów. A wzmianka o potrzebie htots(a właściwie całej rodziny funkcji) również wyjaśnia, że ​​wybranie innej kolejności bajtów nie jest rzeczą, którą należy uprościć, ale może to nieco przyspieszyć.
kasperd

4
Istnieje (nietypowe, ale bardzo często te dni) funkcje htole32(), htole16(), le16toh(), itd., A także funkcje dostępne. Plik, który należy dołączyć, aby je zadeklarować, jest niestety jeszcze mniej standardowy: <endian.h>lub <sys/types.h>zależy od platformy.
trek

Ta odpowiedź jest dobra, ale myślę, że założenie, że wydajność może być krytyczna w danym przypadku, jest prawdopodobnie błędnym założeniem, opartym bardziej na przesądach niż na faktach.
Doc Brown

1
@DocBrown: Zawsze lubię zaznaczać, że protokół X wspiera wybór własnego bajtu przez 30 lat, a przy tak ograniczonych zasobach nikt nigdy nie narzekał, że to problem.
Blrfl 24.04.16

7

To twój protokół.

Nie możesz tego bezpiecznie zignorować. Ale możesz to bezpiecznie oznaczyć. Kontrolujesz klienta i serwer. Kontrolujesz protokół. Czy nie ma sensu nie przejmować się, czy jest to big-endian czy little-endian, o ile wiesz, czy obie strony się zgadzają?

To oznacza narzut. Teraz musisz jakoś zaznaczyć swoją endianizm. Zrób to, a mogę to przeczytać na wszystkim.

Jeśli nie chcesz narzutu danych, a Twój procesor jest znudzony i szuka czegoś do zrobienia, dostosuj się .


6

Więc moje pytanie brzmi: czy mogę bezpiecznie zignorować endianess i po prostu wysłać dane little-endian?

Istnieją dwie interpretacje tego:

  • Jeśli projektujesz swoje aplikacje / protokoły tak, aby zawsze 1 wysyłały little-endian, NIE ignorujesz endianess.

  • Jeśli projektujesz swoje aplikacje / protokoły tak, aby wysyłały / odbierały, czymkolwiek jest natywna endianess, będą one działać tak długo, jak długo będziesz uruchamiać aplikacje na platformach z tą samą natywną endianessą.

    Czy to „bezpieczne” 2 ? To ty musisz osądzić! Ale z pewnością istnieją popularne platformy sprzętowe, które używają little-endian, big-endian lub ... bi-endian.

    Odniesienie:

Jakie są wady?

Oczywistą wadą ignorowania endianess jest to, że jeśli Ty / Twoi użytkownicy musicie uruchamiać swoje aplikacje / protokół między platformami o różnych natywnych endianessach, to masz problem. Aplikacje się zepsują i będziesz musiał je zmienić, aby rozwiązać problem. I radzić sobie z problemami ze zgodnością wersji itp.

Najwyraźniej większość platform obecnej generacji jest natywnie endian, ale 1) niektóre nie są, i 2) możemy tylko zgadywać, co się stanie w przyszłości.


1 - Zawsze ... w tym na platformach, które są natywnie big-endianami.

2 - Rzeczywiście, co oznacza „bezpieczny”? Jeśli pytasz nas o przewidywanie przyszłego kierunku platform sprzętowych ... Obawiam się, że nie jest to obiektywnie możliwe.


3

Endianizm nie jest jedynym czynnikiem. Istnieje wielkość liczb całkowitych, istnieje pakiet struktur, które możesz chcieć wysłać lub odebrać, i tak dalej.

Możesz to wszystko zignorować. Nikt cię nie zmusi. Z drugiej strony, bezpiecznym i niezawodnym sposobem jest udokumentowanie formatu zewnętrznego, a następnie napisanie kodu, który poprawnie odczytuje lub zapisuje format zewnętrzny, bez względu na procesor, język programowania i implementację języka programowania.

Zwykle nie jest to dużo kodu. Ma to jednak ogromną zaletę: osoby czytające Twój kod nie będą podejrzewać, że nie masz pojęcia, nie wiedzą nic o zamianie danych zewnętrznych i piszą kody, którym generalnie nie można ufać.


3

Standardowy stos sieciowy BSD w C ma hton/ ntohfunkcjonalność ( network-to-host/ host-to-network), która rozszerza się na brak operacji na natywnych komputerach sieciowych (big endian). Będziesz potrzebował własnych odpowiedników do tego scenariusza, w którym natywna kolejność bajtów w sieci jest niewielka.

To niezawodny sposób na zrobienie tego.

Byłoby to niekonwencjonalne, ale nie widzę w tym nic złego. Komputery w sieci zawsze otrzymują strumienie bajtów i muszą uzgodnić protokoły interpretacji tych bajtów. To tylko część tego.


3

Różne protokoły używane do przesyłania danych między serwerami używają niewielkich liczb endianowych:

  1. BSON
  2. Bufory protokołów
  3. Capn Proto

Zobacz https://en.wikipedia.org/wiki/Comparison_of_data_serialization_formats , aby uzyskać szczegółowe informacje na temat różnych formatów, z których niektóre mają liczby little-endian, a niektóre mają liczby big-endian.

Nie ma absolutnie nic złego w stosowaniu protokołu opartego na małych liczbach endianowych. Duża maszyna endian jest w stanie równie dobrze odczytywać małe liczby endian, jak mała maszyna endian może czytać duże liczby endian. Wiele osób zrobiło to specjalnie, aby uniknąć dodatkowych kosztów obliczeniowych związanych z dekodowaniem liczb big-endian na małych komputerach endian.

Jeśli zbudujesz swój protokół na jednym z tych istniejących protokołów, nie musisz nawet sam martwić się o problem, który już został rozwiązany. Jeśli zdecydujesz się uruchomić swój kod na platformie big-endian, biblioteki, które implementują te protokoły, automatycznie zadbają o to, aby poprawnie zdekodować wartości.


2

Jednym z przykładów dużego systemu endian jest MIPS stosowany w routerach. Zarówno ARM, jak i MIPS są przełączane przez endian, ale często MIPS jest dużym endianem, ponieważ sprawia, że ​​sprzęt sieciowy jest łatwiejszy (najważniejsza część słowa to część, którą otrzymujesz jako pierwsza i może podjąć decyzję o routingu, zanim otrzymasz resztę słowo zamiast buforować całe słowo).

To zależy od tego, co rozumiesz przez „Linux”, ale jeśli kiedykolwiek będziesz chciał uruchomić aplikację serwera na mniejszym systemie, takim jak router z OpenWRT, być może będziesz musiał rozważyć wsparcie dla dużego endianu.

Jak zwykle, upraszczanie założeń jest optymalną rozsądną optymalizacją, dopóki nie trafisz na coś, co nie pasuje do założeń. Tylko Ty możesz powiedzieć, jak bolesne byłoby ich odprężyć, jeśli kiedykolwiek spotkasz się z takim problemem.


0

Nie sądzę, aby którakolwiek z odpowiedzi była dość precyzyjna. Według Wikipedii endianness to kolejność bajtów zawierających słowo.

Weźmy 4 bajty i zinterpretujmy je jako liczbę całkowitą. Jeden mały system endianowy, bajty będą interpretowane od prawej do lewej i vice-verca w dużym systemie endianowym. Oczywiście ważne jest, aby ustalić, który koniec interpretować int.

Pozwala trochę pomniejszyć współczesne protokoły sieciowe, które mogą używać json lub xml. Żaden z tych formatów nie przeniesie int jako 4 bajty. Przekażą dane jako tekst, który zostanie przeanalizowany jako int po stronie odbierającej.

Więc w końcu endianness nie ma znaczenia, gdy używasz json lub xml. Nadal musimy używać big endian dla nagłówków tcp, dlatego nazywa się to porządkiem bajtów sieciowych, ale większość programistów nie musi codziennie z nimi zadzierać.

Najczęściej stosowanym obecnie kodowaniem jest utf-8, który bywa odporny na problemy związane z endianizmem .

Więc powiedziałbym tak. Bezpieczne jest ignorowanie endianizmu podczas korzystania z formatów tekstowych przesyłanych za pomocą utf-8.


dwa głosy w dół i brak komentarzy. Świetny.
Esben Skov Pedersen

1
Nie byłem downvoter, ale ta odpowiedź wydaje się ignorować / odrzucać całkowicie prawidłowe pytanie. To, że niektóre protokoły są oparte na tekście, nie oznacza, że ​​wszystkie protokoły powinny być.
Peter Green

2
Głosowałem za tym, ponieważ dotyczy to faktu, że format ładunku nie ma nic wspólnego z bazowymi protokołami. Niektórzy ludzie po prostu uwielbiają wrobić wymyślone problemy.
Zdenek

0

Wydaje się, że duże systemy endianów wychodzą. Wiele tradycyjnych uniksów używało Big Endian, ale od lat spada na korzyść Linuksa na x86.

ramię jest bi-endian, ale duży wariant endianu wydaje się być rzadko spotykany.

mips istnieje w obu wariantach. Podobno wariant Big Endian jest najczęściej spotykany w aplikacjach sieciowych (z powodów historycznych protokoły internetowe zazwyczaj używają Big Endian).

ppc był tradycyjnie dużym endianem, z niektórymi częściami obsługującymi oba endiany, ale IBM wydaje się teraz pchać mały tryb endian dla 64-bitowego ppc (ostatnio wypchnął porty ppc64el do Debiana i Ubuntu).

sparc jest zwykle dużym endianem, ale znów wydaje się podupadać.

Jeśli wdrażasz istniejący protokół, oczywiście musisz przestrzegać jego specyfikacji. Jeśli chcesz, aby IETF pobłogosławił twój nowy protokół, prawdopodobnie duży endian będzie łatwiejszy, ponieważ już tego używają w swoich istniejących protokołach, ale IMO dla nowego projektu protokołu „greenfield”, mały endian, jest dobrym rozwiązaniem.

Możesz albo wstawiać makra od samego początku, które nie będą działać na małych systemach endianowych, albo nie możesz zawracać sobie głowy dopóki / chyba, że ​​będziesz musiał przenieść się do dużego systemu endianowego.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.