Влияние порядкового номера на порядок байтов в сети при выполнении reinterpret_cast

#c #networking #endianness #reinterpret-cast

#c #сеть #порядковый номер #переинтерпретация-приведение

Вопрос:

Я пытаюсь преобразовать строковое представление IP-адреса в его байтовое представление и обратно. Для простоты давайте просто поговорим об IPv4-адресах, поскольку порядковый номер одинаково влияет на оба типа IP-адресов.

Учитывая строку в форме "168.212.226.204" , я создаю массив длиной 4, хранящий каждый байт IP-адреса.

Вот код:

 std::array<uint8_t, 4> IpAddress;
const char* addr_str = "168.212.226.204";
struct in_addr in_addr = {};
if (inet_pton(AF_INET, addr_str , amp;in_addr) == 1) // successful conversion
{
    uint8_t* addr = reinterpret_cast<uint8_t*>(amp;(in_addr.s_addr));
    for (size_t i = 0; i < 4; i  )
    {
        IpAddress[i] = addr[i];
    }
  }
 

inet_pton Функция возвращает свой результат в сетевом порядке байтов (big endian), и поскольку я интерпретирую результат как массив байтов, я уверен, что ipAdress[0] он всегда будет содержать MSB IP-адреса. Таким образом, в этом отношении порядковый номер вообще не должен быть проблемой.

Для преобразования из массива байтов в строку я делаю следующее:

 char ip_str[INET_ADDRSTRLEN];
struct in_addr ip_addr = {};
ip_addr.s_addr = *(reinterpret_cast<uint32_t*>(IpAddress.data()));

if (inet_ntop(AF_INET, amp;ip_addr, ip_str, INET_ADDRSTRLEN) != NULL)
{
  return std::string(ip_str);
}
 

Теперь переинтерпретация данных приведет IpAddress к различию uint32_t в зависимости от порядкового номера машины, но поскольку я присваиваю значение an in_addr , просто для вызова inet_ntop , я все еще надеюсь, что этот код будет работать на любой платформе, даже если inet_ntop потребуется сетевой порядок байтов. Правильный ли мой код?

Редактировать: у меня есть машина с небольшим порядковым номером, и там она работает нормально. В итоге возвращаемая строка равна той, с которой я начал ( "168.212.226.204" ) .

Комментарии:

1. В чем смысл этого упражнения? У вас уже есть целое число, представляющее IP-адрес в формате, который требуется для функций стека ip. Для чего нужен массив?

2. Смысл этого упражнения заключается исключительно в том, что я хочу чему-то научиться.

Ответ №1:

Правильный ли мой код?

Нет, но проблема не в порядковом номере.

Во-первых, что касается порядкового номера, 32-битное значение, которое вы загружаете через приведенный указатель, будет находиться в сетевом байтовом порядке. Поскольку вы не интерпретируете и не преобразуете его, а просто сохраняете в другом месте, это сохраненное значение все равно будет в сетевом порядке байтов, подходящем для inet_ntop . Это будет справедливо на любой платформе. Обратите внимание, что тот факт, что вы используете целое число без знака для передачи значения, здесь важен, поскольку целым числам со знаком разрешено иметь представления ловушек, и загрузка произвольного значения из памяти может привести к неопределенному поведению в этом случае.

Проблема в том, что для разыменования приведенного указателя требуется соответствующее выравнивание памяти, чего ваш код не гарантирует. Вам необходимо либо принудительно выполнить выравнивание IpAddress до 4 байтов (например, с помощью alignas ), либо использовать другой метод передачи значения, который не требует выравнивания, например memcpy . Последнее более предпочтительно, поскольку оно более четко выражает ваше намерение.

 std::memcpy(amp;ip_addr.s_addr, IpAddress.data(), IpAddress.size());
 

Комментарии:

1. Вау, большое вам спасибо! Это идеальный ответ. Я не знал, что здесь память должна быть выровнена по байтам.