2010-11-18 13 views
5

Piszę grę wieloplatformową z funkcjami sieciowymi (używając SFML i RakNet) i doszedłem do punktu, w którym skompilowałem serwer na moim serwerze Ubuntu i dostałem klienta na moim Macu. Cały rozwój odbywa się na moim Macu, więc początkowo testowałem na tym serwerze i działało dobrze.Tworzenie i używanie struktury wieloplatformowej w C++

Wysyłam struct s przez sieć, a następnie po prostu odsyłaję je z powrotem od char * do (na przykład) inet::PlayerAdded. Teraz to działa dobrze (w przeważającej części), ale moje pytanie brzmi: Czy to zawsze zadziała? Wydaje się to bardzo delikatnym podejściem. Czy struktura będzie zawsze układana tak samo, nawet na innych platformach, na przykład Windows? Co byś polecił?

#pragma pack(push, 1) 
struct Player 
{ 
    int dir[2]; 
    int left; 
    float depth; 
    float elevation; 
    float velocity[2]; 
    char character[50]; 
    char username[50]; 
}; 

// I have been added to the game and my ID is back 
struct PlayerAdded: Packet 
{ 
    id_type id; 
    Player player; 
}; 
#pragma pack(pop) 
+2

Do seryjnego downwizera, z konkretnego powodu? (Zdanie Steve'a wydaje się szczególnie szokujące) – KevinDTimm

+0

W takich miejscach punkty są bez znaczenia. Nie martwiłbym się tym. –

+1

Nie dbam o punkty, chcę wiedzieć, dlaczego każda odpowiedź (z wyjątkiem jednej, najmniej użytecznej ze wszystkich) została odrzucona. Zwłaszcza gdy jedna z (downvoted) odpowiedzi była wyraźnie najlepsza. – KevinDTimm

Odpowiedz

5

Podobnie jak wiele innych odpowiedzi, odradzam wysyłanie surowych danych binarnych, jeśli można tego uniknąć. Coś takiego jak Boost serial lub Google Protobuf wykona dobrą robotę bez zbytniego narzutu.

Ale na pewno można tworzyć wieloplatformowe struktury binarne, odbywa się to cały czas i jest bardzo ważnym sposobem wymiany danych. Nakładanie "struktury" na te dane ma sens. Musisz jednak bardzo uważać na układ, na szczęście większość kompilatorów daje ci wiele możliwości. "paczka" jest jedną z takich opcji i wiele dba.

Należy również zadbać o rozmiary danych. Proste obejmują stdint.h i użyj typów o ustalonym rozmiarze, takich jak uint32_t. Uważaj na wartości zmiennoprzecinkowe, ponieważ nie wszystkie architektury będą miały tę samą wartość, ponieważ prawdopodobnie będą to operacje 32-bitowe. Również w przypadku endianess większość architektur będzie korzystała z tego samego, a jeśli tego nie zrobi, po prostu odwrócisz go na klienta, który jest inny.

+0

Mimo że większość odpowiedzi była naprawdę wspaniała, myślę, że to właśnie ona najbardziej mi pomogła. Myślę, że dam mu szansę, a jeśli kiedykolwiek uderzę w barierę tą metodą, będę wiedział, czego użyć. – ErikPerik

9

To nie zadziała, jeżeli (między innymi), próbując zrobić z ostrokońcej maszynie do big-endian maszyny jako poprawna int reprezentacja zostanie odwrócony między nimi.

Mogłoby się to również nie udać, jeśli wyrównanie lub pakowanie konstrukcji ulegnie zmianie z urządzenia na maszynę. Co jeśli masz jakieś 64-bitowe maszyny, a niektóre 32-bitowe?

Musisz użyć odpowiedniej biblioteki do serializacji, takiej jak Boost.Serialization lub Google Protocol Buffers, aby upewnić się, że masz protokół transmisji (znany także jako format danych przenośnych), który może zostać pomyślnie zdekodowany niezależnie od sprzętu.

Jedną z zalet buforów protokołów jest możliwość przezroczystego kompresowania danych przy użyciu strumienia zgodnego z ZLIB, który jest również zgodny ze strumieniami protobuf. Tak naprawdę to zrobiłem, działa dobrze. Wyobrażam sobie, że inne strumienie dekoratorów mogą być używane w analogiczny sposób w celu ulepszenia lub zoptymalizowania podstawowego protokołu przewodowego w razie potrzeby.

2

Odpowiedź na "... ułożoną tak samo na innych platformach ..." jest generalnie nie. Dzieje się tak, nawet jeśli rozwiązano takie problemy, jak różne procesory i/lub różne endiancje.

Różne systemy operacyjne (nawet na tej samej platformie sprzętowej) mogą używać różnych reprezentacji danych; jest to zwykle nazywane "platformą ABI" i różni się między np. 32-bitowe/64-bitowe systemy Windows, 32-bitowy/64-bitowy system Linux, MacOSX.

"Pakiet #pragma" jest tylko w połowie drogi, ponieważ poza ograniczeniami wyrównania mogą występować różnice wielkości typów danych. Na przykład "długi" w 64-bitowym systemie Windows to 32-bitowy, a 64-bitowy w systemach Linux i MacOSX.

Problem ten oczywiście nie jest nowy i został już rozwiązany w przeszłości - standard zdalnego wywoływania procedur (RPC) zawiera mechanizmy definiujące struktury danych w sposób niezależny od platformy i sposób kodowania/dekodowane "bufory" reprezentujące te struktury. Nazywa się "XDR" (eXternal Data Representation). Zobacz dokument RFC1832. W miarę programowania, koło to było wielokrotnie odkrywane na nowo; niezależnie od tego, czy konwertujesz do formatu XML, sam wykonuj niski poziom pracy z XDR, użyj google :: protobuf, boost lub Qt :: Variant, jest dużo do wyboru.

Strona czysto implementacyjna: Dla uproszczenia wystarczy przyjąć, że "unsigned int" wszędzie jest 32-bitowe wyrównane na granicy 32-bitowej; jeśli możesz zakodować wszystkie swoje dane jako tablicę 32-bitowych wartości, jedynym problemem z eksternalizacją, z którym musisz sobie poradzić, jest endianness.