[C++] send(), recv() - przesyłanie struktury

Problemy dotyczące programowania.

Moderatorzy: Moderatorzy, Administratorzy

Macok
Użytkownik
Posty: 115
Rejestracja: 2008-07-09, 14:08

[C++] send(), recv() - przesyłanie struktury

Post autor: Macok »

Mam pytanie związane z przesyłaniem struktur za pomocą funkcji send() i recv().
Ostatnio dowiedziałem się, że struktury w pamięci mogą być różnie przechowywane (pakowane).
Np. w takiej strukturze:

Kod: Zaznacz cały

struct Struktura{
int a;
short b;
int c;
}
Nie wiadomo dokładnie jakie będą odstępy między zmiennymi.
Z tego co wiem domyślnie wielkość zmiennych jest zaokrąglana do 4 czyli za short b będzie 2 bajty wolnego miejsca.
W każdym razie załóżmy, że komputer A wysyła strukturę:

Kod: Zaznacz cały

Struktura obiekt;
send(socket, (char*)&obiekt, sizeof(Struktura), 0);
A komputer B odbiera:

Kod: Zaznacz cały

Struktura obiekt;
recv(socket, (char*)&obiekt, sizeof(Struktura), 0
Co się stanie, jeżeli te komputery inaczej pakują struktury w pamięci?

Gdybym musiał napisać teraz napisać taki program zapewne wysyłałbym wszystkie składowe struktury po kolei, ale bezpiecznie jak wysłać i odebrać wszystkie na raz?
Ostatnio zmieniony 2009-07-11, 20:17 przez Macok, łącznie zmieniany 1 raz.
Awatar użytkownika
dienet
Moderator
Posty: 2105
Rejestracja: 2007-07-24, 18:58
Lokalizacja: Racibórz/Rybnik

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: dienet »

Pozdr0
dienet
[img]http://i164.photobucket.com/albums/u19/slawek15/kotekeo0lq3.jpg[/img]
chimi
Użytkownik
Posty: 117
Rejestracja: 2005-05-01, 15:49

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: chimi »

Macok pisze:Z tego co wiem domyślnie wielkość zmiennych jest zaokrąglana do 4 czyli za short b będzie 2 bajty wolnego miejsca.
Na x86 - tak, ale już na x86_64 wyrównanie będzie do 8 (16?). I pewnie można to zmienić (wyłączyć) stosownymi opcjami kompilatora lub markami.
Macok pisze:Co się stanie, jeżeli te komputery inaczej pakują struktury w pamięci?
Dane nie trafią na swoje miejsce - będą śmieci w strukturze, może też dojść do wyjścia poza zaalokowaną pamięć.
Macok pisze:Gdybym musiał napisać teraz napisać taki program zapewne wysyłałbym wszystkie składowe struktury po kolei, ale bezpiecznie jak wysłać i odebrać wszystkie na raz?
GCC pozwala na __attribute__ ((__packed__)) po definicji struktury, co usuwa wyrównanie, ale może mieć negatywny wpływ na wydajność (jeśli wykonujesz dużo operacji na tych strukturach).
Ostatnio zmieniony 2009-07-11, 21:20 przez chimi, łącznie zmieniany 1 raz.
Macok
Użytkownik
Posty: 115
Rejestracja: 2008-07-09, 14:08

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: Macok »

@Offtopic
chimi pisze:Na x86 - tak, ale już na x86_64 wyrównanie będzie do 8 (16?).
A dlaczego tak się dzieje?
Czemu w architekturze x86_64 nie mogli zrobić wyrównania do 4?

@Topic
W tym poradniku co podał dienet znalazłem taką dyrektywę kompilatora (tak to się chyba nazywa) pragma pack:
http://msdn.microsoft.com/en-us/library ... S.80).aspx
Można nią ustawić do ilu bajtów pamięć będzie wyrównywana.
Jeżeli ustawię to na taką samą wartość w kliencie i w serwerze to będzie działać?
I chyba powinienem też zamiast int użyć int32_t żeby miec pewność, że wielkość int będzie na wszystkich komputerach taka sama?
Ostatnio zmieniony 2009-07-12, 09:12 przez Macok, łącznie zmieniany 2 razy.
Awatar użytkownika
dienet
Moderator
Posty: 2105
Rejestracja: 2007-07-24, 18:58
Lokalizacja: Racibórz/Rybnik

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: dienet »

Macok pisze:
@Topic
W tym poradniku co podał dienet znalazłem taką dyrektywę kompilatora (tak to się chyba nazywa) pragma pack:
http://msdn.microsoft.com/en-us/library ... S.80).aspx
Można nią ustawić do ilu bajtów pamięć będzie wyrównywana.
Jeżeli ustawię to na taką samą wartość w kliencie i w serwerze to będzie działać?
I chyba powinienem też zamiast int użyć int32_t żeby miec pewność, że wielkość int będzie na wszystkich komputerach taka sama?
Wydaje sie ze tak, ale mysle ze wysylanie struktur via socket nie jest wskazane. Opracuj jakis protokol i go uzywaj.
Pozdr0
dienet
[img]http://i164.photobucket.com/albums/u19/slawek15/kotekeo0lq3.jpg[/img]
Pajaczek
Użytkownik
Posty: 1439
Rejestracja: 2006-08-03, 13:16
Lokalizacja: Winny Gród

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: Pajaczek »

Macok pisze:Czemu w architekturze x86_64 nie mogli zrobić wyrównania do 4?
Może dlatego, że 64 bity to 8 bajtów a nie 4.

Dołącz sobie do struktury pole size (najlepiej jako pierwsze), albo przed wysłaniem struktury zapakuj ją do większej, zawierającej size + Twoja struktura. Odbierając będziesz mógł sprawdzić ilu bajtów należy się spodziewać (i ew. dopytać o resztę).
Macok
Użytkownik
Posty: 115
Rejestracja: 2008-07-09, 14:08

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: Macok »

Pajaczek pisze:Odbierając będziesz mógł sprawdzić ilu bajtów należy się spodziewać (i ew. dopytać o resztę).
Tego sposobu raczej nie zastosuje, bo wielkość struktury tak naprawdę nic mi nie daje, jeżeli odstępy między składowymi struktury są inne na 2 komputerach.
Ale nie ważne, będę wysyłał składowe struktury po kolei albo stosował #pragma pack

Dzieki za pomoc!
Awatar użytkownika
mina86
Moderator
Posty: 3343
Rejestracja: 2004-06-14, 21:58
Lokalizacja: Linux 5.x x86_64
Kontakt:

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: mina86 »

Po pierwsze, jeżeli przesyłasz dane pomiędzy różnymi komputerami nie wiesz jakie wielkości będą miały poszczególne typy. Zazwyczaj int i short będą miały takie rozmiary jak się spodziewasz, ale na innych architekturach short i int mogą mieć po 16 bitów.

Po drugie, na różnych architekturach liczby kodowane są w różny sposób (pomijam raczej całkowicie teoretyczne rozważania, iż zmienne ze znakiem nie muszą być kodowane w U2) -- konkretnie chodzi o kolejność bajtów. Na jednym komputerze wartość 0x1234 może być zapisana w pamięci jako 0x12, 0x23, a na innym 0x23, 0x12.

Reasumując, aby przesyłać struktury, należy je ręcznie pakować do bufora, np.:

Kod: Zaznacz cały

struct foo {
	int a;
	short b;
	int c;
};


static void put16(unsigned char *buf, short val) {
	buf[0] = (val >> 8) & 0xff;
	buf[1] = val & 0xff;
}

static void put32(unsigned char *buf, int val) {
	put16(buf, (val >> 16) & 0xffff);
	put16(buf + 2, val & 0xffff);
}

static short get16(unsigned char *buf) {
	return ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
}

static int get32(unsigned char *buf) {
	return (get16(buf) << 16) | get16(buf + 2);
}


/* Wysyalnie */
{
	struct foo foo;
	unsigned char buf[10];
	put32(buf    , foo.a);
	put16(buf + 4, foo.b);
	put32(buf + 6, foo.c);
	send(fd, buf, 10, 0);
}


/* Odbieranie */
{
	struct foo foo;
	unsigned char buf[10];
	recv(fd, buf, 10, 0);
	foo.a = get32(buf    );
	foo.b = get16(buf + 4);
	foo.c = get32(buf + 6);
}
Zastrzegam sobie prawo nieanalizowania postów pisanych niepoprawną polszczyzną.
Post generated automatically by A.I. system code name ‘mina86’ in response to the previous one.
Macok
Użytkownik
Posty: 115
Rejestracja: 2008-07-09, 14:08

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: Macok »

mina86 pisze:

Kod: Zaznacz cały

static void put16(unsigned char *buf, short val) {
	buf[0] = (val >> 8) & 0xff;
	buf[1] = val & 0xff;
}
Nie do końca rozumiem o co tutaj chodzi.
Przesuwasz bity w val o 8 w prawo, czyli w buf[0] będzie starsze 8 bitów z val.
Ale po co to & 0xff?
Czy takie działanie:

Kod: Zaznacz cały

buf[0] = (val >> 8) & 0xff;
nie jest przypadkiem równoważne temu?

Kod: Zaznacz cały

buf[0] = (val >> 8);
Ostatnio zmieniony 2009-07-28, 09:48 przez Macok, łącznie zmieniany 1 raz.
Awatar użytkownika
mina86
Moderator
Posty: 3343
Rejestracja: 2004-06-14, 21:58
Lokalizacja: Linux 5.x x86_64
Kontakt:

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: mina86 »

W ogólnym przypadku, char może mieć teoretycznie więcej niż 8 bitów i wówczas oba zapisy nie będą równoważne, a tam, gdzie są, kompilator sobie z tym poradzi.
Ostatnio zmieniony 2009-07-29, 13:46 przez mina86, łącznie zmieniany 1 raz.
Zastrzegam sobie prawo nieanalizowania postów pisanych niepoprawną polszczyzną.
Post generated automatically by A.I. system code name ‘mina86’ in response to the previous one.
Awatar użytkownika
Hannibal
Moderator w st. spocz.
Posty: 1644
Rejestracja: 2004-06-08, 16:03
Lokalizacja: Łódź

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: Hannibal »

Nie ma co pisać własnych funkcji:

Kod: Zaznacz cały

man byteorder
man endian
[size=75]Hannibal@current@2.6.X[/size]
acek
Użytkownik
Posty: 47
Rejestracja: 2006-09-26, 21:27
Kontakt:

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: acek »

mina86 pisze:char może mieć teoretycznie więcej niż 8 bajtów
Bitów?
Awatar użytkownika
mina86
Moderator
Posty: 3343
Rejestracja: 2004-06-14, 21:58
Lokalizacja: Linux 5.x x86_64
Kontakt:

Re: [C++] send(), recv() - przesyłanie struktury

Post autor: mina86 »

Nie ma co pisać własnych funkcji:
Tylko, że one robią troszkę coś innego. Aby zapisać potem do pamięci trza by było rzutować i wydziwiać różne inne czary nie widy. Pojawia się wówczas teoretyczny problem z wyrównaniem i inne takie. Choć oczywiście warto przyjrzeć się funkcjom na stronach, które podałeś. Swoją drogą, ciekawe czy gcc już potrafi optymalizować podany przeze mnie kod na architekturach big-endian.
Hannibal pisze:Bitów?
Oczywiście, już poprawiłem.
Zastrzegam sobie prawo nieanalizowania postów pisanych niepoprawną polszczyzną.
Post generated automatically by A.I. system code name ‘mina86’ in response to the previous one.
ODPOWIEDZ