Почему первые упакованные данные в структуре с прямым порядком байтов, а остальные - с прямым порядком байтов? - PullRequest
2 голосов
/ 17 октября 2019
import struct
port = 1331
fragments = [1,2,3,4]
flags = bytes([64])
name = "Hello World"

data = struct.pack('HcHH', port, flags, len(fragments), len(name))

print(int.from_bytes(data[3:5], byteorder='big'))
print(int.from_bytes(data[5:7], byteorder='big'))
print(int.from_bytes(data[0:2], byteorder='little'))

Когда я их так печатаю, они выходят правильно. Кажется, порт находится в порядке с прямым порядком байтов, в то время как len(fragments) и len(name) находятся в порядке с прямым порядком байтов. Если я также использую в качестве порта порядок байтов, то он получит неправильное значение.

Так почему struct ведет себя так? Или я что-то упустил?

Ответы [ 2 ]

2 голосов
/ 17 октября 2019

Из-за буквы "c" в середине буквы "H" происходит странное выравнивание. Вы можете увидеть это с помощью calcsize:

>>> struct.calcsize('HcHH')
8
>>> struct.calcsize('HHHc')
7

Таким образом, ваши данные не выровнены, как вы думали. Правильная распаковка:

print(int.from_bytes(data[4:6], byteorder='little'))
# 4
print(int.from_bytes(data[6:], byteorder='little'))
# 11

Оказывается, что случайно добавленный байт 'c' равен '\ x00', и ваша цепочка байтов была правильной в старшем порядке:

>>> data
b'3\x05@\x00\x04\x00\x0b\x00'
        ^^^^
        this is the intruder
0 голосов
/ 17 октября 2019

По умолчанию ваш вызов pack эквивалентен следующему:

struct.pack('@HcHH', port, flags, len(fragments), len(name))

Результат выглядит следующим образом (напечатано '.'.join(f'{x:02X} for x in data')):

33.05.40.00.04.00.0B.00
 0  1  2  3  4  5  6  7

Число 4 закодировано в байтах 4 и 5 в младшем порядке, а 11 закодировано в байтах 6 и 7. Байт 3 является байтом заполнения, вставленным pack для правильного выравнивания следующего shorts на четной границе.

По документам :

Примечание По умолчанию результат упаковки данной структуры C включаетбайты заполнения для обеспечения правильного выравнивания для задействованных типов Си;аналогично, выравнивание учитывается при распаковке. Это поведение выбрано так, чтобы байты упакованной структуры точно соответствовали расположению в памяти соответствующей структуры Си. Чтобы обрабатывать независимые от платформы форматы данных или опускать неявные байты пэдов, используйте размер и выравнивание standard вместо размера и выравнивания native: подробности см. Порядок, размер и выравнивание байтов .

Для удаления байта выравнивания и обоснования ваших предположений о позициях байтов при сохранении собственного порядка байтов используйте

struct.pack('=HcHH', port, flags, len(fragments), len(name))

. Вы также можете использовать фиксированный порядок байтов с помощью < или> в качестве префикса.

«Правильное» решение - использовать unpack, чтобы вернуть свои номера, чтобы вам не приходилось беспокоиться о порядке байтов, заполнении или чем-либо ещеиначе, действительно.

...