Ramm
> Т.е. с помощью memcpy я заталкиваю структуру со своими интами и флоатами в
> массив char, отправляю его, а на той стороне (например, это UDP-пакет)
> разворачиваю обратно в такую же структуру? И все корректно прочитается?
> Но проблема с интами все равно останется? Так? Т.е. не факт, что они корректно
> восстановятся?
Не так. Просто заполняешь сишную структуру (ты должен знать ее двоичное представление в памяти, не любая структура годится). Без всяких memcpy. Берешь указатель на структуру, прямым преобразованием типов говоришь что это указатель на массив байтов и подсовываешь его функциям передачи по сети. Никакого копирования не происходит, просто явно объявляешь что тип у этого указателя надо считать другим.
Просто инты не передавай по сети. Зачем они тебе? Заведи какие-нибудь му_int32, му_int16, му_int64 и только ими в сети пользуйся. Если порядок байт надо поменять, делай данные private и обкладывай их конвертирующими функциями доступа.
Ramm
> С флоатами такой проблемы, как порядок байтов, быть не может? Завернул через
> memcpy в 4 char-а, а на другой виндовс-машине развернул, и все должно работать
> корректно?
С каких пор менять порядок байт перед отправкой данных стала большой проблемой? Во флоат пойтах порядок байт, конечно, зависит от машины. Но бинарно на всех машинах они хранится одинаково. Нет такого, чтобы где-то float32 или float64 был не 32 или не 64 бита. Сам FPU может считать их как хочет, хоть пусть у него будут 8 закольцованных 80 битных регистров, вообще все равно, главное хранится они будут так как положено по стандарту. А поменять порядок байт на little endian машине не сложно
Zab
> Просто заполняешь сишную структуру (ты должен знать ее двоичное представление
> в памяти, не любая структура годится).
Можно немного поподробнее, что значит "знать двоичную структуру"
Я добавил структуру:
struct PERSON { float age; float weight; float name[25]; };
А вот отправка:
char *sendbuf; char recvbuf[512]; PERSON p1; p1.age = 20.0; p1.weight = 23.0; p1.name[0] = 345; sendbuf =(char*) &p1; int count_bytes = send( ConnectSocket, sendbuf, sizeof( PERSON), 0);
Он отправил 108 байт...
Я все верно делаю? Правильный тип структуры? А данные в ней не перемешаются?
На сервере получаю так:
count_bytes = recv(ClientSocket, recvbuf, recvbuflen, 0); char tmp[108]; for ( int i = 0; i < 108; i++) { tmp[i] = recvbuf[i]; } PERSON *p1; p1 =( PERSON *) tmp; cout << p1->age << " " << p1->weight << " " << p1->name[0] << endl;
Ну и все вроде пришло в норме.
Ramm
Да, ты отправил структуру. Но не описал как она выравнена по памяти (эти средства языка компиляторозависимые).
Отправиться, кстати, могут не все байты, send может отправить только часть заказанного, сколько именно она сообщает через возвращаемое значение, остальное надо доотправлять дополнительными send'ами.
Ramm
> Я добавил структуру:
Со структурами поосторожнее, есть еще такая штука как выравнивание данных в памяти.
ZeroCool++
> С каких пор менять порядок байт перед отправкой данных стала большой проблемой?
> Во флоат пойтах порядок байт, конечно, зависит от машины. Но бинарно на всех
> машинах они хранится одинаково.
Я к тому, что не будет такой проблемы, что я перевел 255 в char[4], 255 стала выглядеть как -1 0 0 0, на другой машине я делаю обратное действие и получу -23471 (например), только потому, что там обратный порядок преобразования или в памяти оно хранится по-другому???
Элементарный пример, если я создам сокет на C#, приму 4 байта (255 в виде 4 чаров), то я смогу привести их к нормальному виду, т.е. получить 255?
ZeroCool++
> выравнивание данных в памяти.
Даже если все данные одного типа?
Помощь в определении порядка байт:
https://ideone.com/Bc0McV
Тебе чтобы загнать все в char[] все равно нужно знать в каком порядке их туда заганять. Потому что ты можешь заганять туда в прямом, а считывать в обратном или наоборот.
Просто на little endian машине ты должен при записи в свой char[] явно у int32 отзеркалить 4 байта, у int64 - 8 байт и т.д. При считывании на litle endian машинах тоже самое но только обратно
Ramm
> Даже если все данные одного типа?
Да
Пример:
struct
{
uint32_t i1;
uint32_t i2;
uint32_t i3;
};
на x86 и на x64 машинах размер структуры будет разным
Тоска...
Почему я просто не могу через memcpy запихать все свои floatы в 4-байтные массивы, те в свою очередь запихать в один большой массив (эту задачу можно упростить, но результат один), затем, в начале и в конце массива поставить 255 и 756 (например, конечно, т.е. последние и первые 4 байта этого большого массива - это как-бы границы), при разборе пакета, т.е. полученного массива, я смотрю на первые и последние 4 байта, и если 255 756 не получается, я пробую разобрать первые и последние 4 байта в обратном порядке, если и это не помогает, то я пробую разобрать массив с конца, в обратном порядке...
Что в этом коварном плане плохого?
4 байта для всех флоатов, на любой машине я должен из массива char a[4] = {-1 0 0 0} получить 255, ну или попробовать в другом порядке, с конца, вдруг машина хранит в другом порядке.
Не может же 255 выглядеть как 1 2 3 11, например.
Ramm
Порядок байт касается интов, но не флоатов. Как хранятся флоаты определяется стандартом. Никто не обещал, что этот стандарт будет на всех компах, но на сегодняшний день ничего лучше нет и он везде.
Zab
МНЕ ДАЖЕ ПРОВЕРЯТЬ порядок не надо? Ни начало, ни конец. Записал и отправил?
Я про udp-пакет...
ZeroCool++
> на x86 и на x64 машинах размер структуры будет разным
И почему он будет разный?
Ramm
Как хранятся в памяти флоты никаким стандартом не определяется. Их нужно читать/писать точно так же, как и целые числа - побайтный складывая из потока.
ZeroCool++
> Просто на little endian машине ты должен при записи в свой char[] явно у int32 отзеркалить 4 байта, у int64 - 8 байт и т.д. При считывании на litle endian машинах тоже самое но только обратно
Little-endian — это наиболее математически естественное представление чисел, и к счастью сейчас уже не осталось ущербных big-endian архитектур.
Так что наиболее оптимальное поведение в 2018 — считать, что везде little-endian, а на big-endian воткнуть assert, чтобы сразу падало.
> на x86 и на x64 машинах размер структуры будет разным
:facepalm:
Zab
> Порядок байт касается интов, но не флоатов.
Вообще-то он касается всего, причем он не обязан быть одинаковым для целых и вещественных.
К счастью, сейчас можно заложиться на little-endian и не заморачиваться.
Zab
> Порядок байт касается интов, но не флоатов. Как хранятся флоаты определяется
> стандартом. Никто не обещал, что этот стандарт будет на всех компах, но на
> сегодняшний день ничего лучше нет и он везде.
Для флоатов тоже. А стандарт есть стандарт, он везде одинаковый, даже на ЦП где нет математического сопроцессора на кристалле (как на пример в старых аэремках) их все равно реализуют по стандарту (возможно, конечно, говорят, без поддержки денормализованных чисел), а вся арифметика флот пойтов будет реализовываться через целые числа ЦП, это будет медленно работать, но будет и храниться они будут одинаково.
}:+()___ [Smile]
> Little-endian — это наиболее математически естественное представление чисел
Никто не спорит, он для вычислительной техники наиболее подходящий, нежели big-endian. Но исторически сложилось, что сетевой порядок байт big endian, а не little endian.
}:+()___ [Smile]
> и к счастью сейчас уже не осталось ущербных big-endian архитектур
Спорный утверждение, есть куча процессоров с переменным порядком байтов (те же аэремки).
Но почти все работает как little endian, так что - да, замарачиваться с big endian не имеет смысла
Тема в архиве.