Mam proces, który słucha transmisji rozgłoszeniowej UDP i odczytuje dane jako niepodpisany znak *.
Mam specyfikację, która wskazuje pola w obrębie tego niepodpisanego znaku *.
Pola są zdefiniowane w specyfikacji z typem i rozmiarem.
Typy: uInt32, uInt64, unsigned int i jednobajtowy łańcuch.
W przypadku ciągu jednobajtowego mogę jedynie uzyskać dostęp do przesunięcia pola w niepodpisanym znaku * i przesłać do znaku, na przykład:
char character = (char)(data[1]);
Pojedynczy bajt uint32 wykonuję następujące czynności, które również wydają się działać:
uint32_t integer = (uint32_t)(data[20]);
Jednak w przypadku konwersji wielobajtowych wydaje mi się, że utknąłem.
Jak przekonwertować kilka bajtów z rzędu (podciąg z data
) do odpowiedniego typu danych?
Czy bezpieczne jest zawijanie danych w łańcuchu (w celu wykorzystania funkcji podłańcuchowych)? Martwię się o utratę informacji, ponieważ będę musiał rzucić niepodpisany znak * na char *, na przykład:
std::string wrapper((char*)(data),length); //Is this safe?
Próbowałem coś takiego:
std::string wrapper((char*)(data),length); //Is this safe?
uint32_t integer = (uint32_t)(wrapper.substr(20,4).c_str()); //4 byte int
Ale to nie działa.
Myśli?
Aktualizacja
Próbowałem przesunięcia bitowego sugerowania:
void function(const unsigned char* data, size_t data_len)
{
//From specifiction: Field type: uInt32 Byte Length: 4
//All integer fields are big endian.
uint32_t integer = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]);
}
To niestety daje mi śmieci (ten sam numer dla każdego połączenia - od callbacka).
Odpowiedzi:
2 dla odpowiedzi № 1Myślę, że powinieneś być bardzo jednoznaczny, i nie po prostu rób "sprytne" sztuczki za pomocą rzutów i wskaźników. Zamiast tego napisz taką funkcję:
uint32_t read_uint32_t(unsigned char **data)
{
const unsigned char *get = *data;
*data += 4;
return (get[0] << 24) | (get[1] << 16) | (get[2] << 8) | get[3];
}
To wyodrębnia pojedynczą wartość uint32_t z bufora o niepodpisanym znaku i zwiększa wskaźnik bufora do punktu na następnym bajcie danych w buforze.
Zakłada to dane big-endian, musisz mieć dobrze zdefiniowaną koncepcję trybu endianowego bufora, aby go zinterpretować.
2 dla odpowiedzi nr 2
Zależy od kolejności bajtów protokołu, w przypadku wielobajtowej lub tzw. Kolejności bajtów sieciowych:
uint32_t i = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
0 dla odpowiedzi № 3
Bez komentowania, czy jest to dobry pomysł, czy nie, powodem, dla którego nie działa on dla ciebie, jest to, że wynikiem wrapper.substring (20,4) .c_str () jest (uint32_t *), a nie (uint32_t). Jeśli więc:
uint32_t * integer = (uint32_t *) (wrapper.substr (20,4) .c_str (); powinno działać.
0 dla odpowiedzi nr 4
uint32_t integer = ntohl(*reinterpret_cast<const uint32_t*>(data + 20));
lub (obsługuje problemy z wyrównaniem):
uint32_t integer;
memcpy(&integer, data+20, sizeof integer);
integer = ntohl(integer);
0 dla odpowiedzi № 5
Wskaźnik:
uint32_t n = *(uint32_t*)&data[20];
Jednak napotkasz problemy na różnych architekturach endian. Rozwiązanie z przesunięciami bitowymi jest lepsze i spójne.
std::string wrapper((char*)(data),length); //Is this safe?
To powinno być bezpieczne, ponieważ podałeś długość danych. Z drugiej strony, jeśli to zrobiłeś:
std::string wrapper((char*)data);
Długość łańcucha zostanie określona w miejscu, w którym wystąpi pierwszy 0 bajtów, a użytkownik prawdopodobnie zetnie trochę danych.