Проблема с использованием struct.pack и struct.unpack - PullRequest
0 голосов
/ 01 марта 2019

Вот мой фрагмент кода:

import struct


class _packet:

    def __init__(self, payload):  
        self.version = 1
        self.syn = False
        self.fin = False
        self.reset = False
        self.hasOpt = False
        self.ack = 0
        self.payload = payload
        return

    def pack(self):
        return struct.pack('????i' + str(len(self.payload)) + 's', self.syn, self.fin, self.reset, self.hasOpt,self.ack, bytes(self.payload, 'utf-8'))

    def unpack(self):
        unpackedData = bytearray()
        return struct.unpack('????i5s', unpackedData)

def main():
    packet = _packet("Hello")
    packet.ack = 249
    packet.syn = True
    packet.fin = True
    packet.reset = True
    packedData = packet.pack()
    print(packedData)
    unpackedData = packet.unpack()
    print(unpackedData)

if __name__== "__main__":
    main()

Моя цель - создать пакет, использовать struct.pack для его кодирования и отправить его через сокет, а затем использовать unpack, чтобы вернуть данные вкортеж, чтобы я мог извлечь необходимые биты из него.В моем пакете нет нужных битов, потому что это минимальный пример использования пакетов.Как только я выполняю строку

packedData = packet.pack()
print(packedData)

, я получаю это в качестве своего вывода:

b'\x01\x01\x01\x00\xf9\x00\x00\x00Hello'

Кажется, что это то, что я ожидаю, но проблема возникает, когда я запускаю следующие строки:

 unpackedData = packet.unpack()
 print(unpackedData)

Я получаю следующую ошибку:

unpack requires a bytes object of length 13

Если я заменю неупакованные данные на байт-массив длины 13, я получу следующий вывод в качестве распакованных данных:

(False, False, False, False, 0, b'\x00\x00\x00\x00\x00')

Это, очевидно, неправильно, поскольку он не сохранил мои значения и просто выглядит как другой экземпляр пакета.

Я неправильно создаю свои объекты пакета?Или я неправильно упаковываю и распаковываю свои данные?

Ответы [ 2 ]

0 голосов
/ 01 марта 2019

Несколько замечаний:

  1. Вы можете предварительно создать объект Struct для упаковки и распаковки заголовка
  2. Полезная нагрузка легче упаковать и распаковать отдельно, отдельноиз шапки.Он может быть добавлен к упакованному заголовку и извлечен из пакета с использованием нарезки.
  3. unpack должен быть методом класса, который принимает объект bytes и возвращает экземпляр Packet.
  4. Создание этого класса данных позволяет избежать написания большого количества шаблонов для метода __init__.

from dataclasses import dataclass
from typing import ClassVar
import struct

@dataclass
class Packet:

    header : ClassVar[struct.Struct] = struct.Struct('????i')
    payload: str
    syn: bool = False
    fin: bool = False
    reset: bool = False
    has_opt: bool = False
    ack: int = 0

    def pack(self):
        return self.header.pack(
                   self.syn, 
                   self.fin,
                   self.reset,
                   self.has_opt,
                   self.ack
               ) + self.payload.encode('utf-8')

    @classmethod
    def unpack(cls, data: bytes):
        payload = data[cls.header.size]
        syn, fin, reset, has_opt, ack = cls.header.unpack_from(data)
        return Packet(
                   payload.decode('utf'),
                   syn,
                   fin,
                   reset,
                   has_opt,
                   ack)
0 голосов
/ 01 марта 2019

Если вы хотите, чтобы struct.unpack возвратил данные, которые вы передали struct.pack, то аргумент, который вы передаете struct.unpack, должен быть объектом, возвращаемым из struct.pack.Прямо сейчас вы даете ему пустой байтовый массив, поэтому вы возвращаете пустые данные.

Одно из возможных решений - передать упакованные данные в качестве аргумента _packet.unpack, который вы затем передаете struct.unpack.

import struct


class _packet:

    def __init__(self, payload):  
        self.version = 1
        self.syn = False
        self.fin = False
        self.reset = False
        self.hasOpt = False
        self.ack = 0
        self.payload = payload
        return

    def pack(self):
        return struct.pack('????i' + str(len(self.payload)) + 's', self.syn, self.fin, self.reset, self.hasOpt,self.ack, bytes(self.payload, 'utf-8'))

    def unpack(self, data):
        header_size = 8 #four one-byte bools and one four-byte int
        return struct.unpack('????i' + str(len(packed_data)-header_size) + 's', data)

def main():
    packet = _packet("Hello")
    packet.ack = 249
    packet.syn = True
    packet.fin = True
    packet.reset = True
    packedData = packet.pack()
    print(packedData)
    unpackedData = packet.unpack(packedData)
    print(unpackedData)

if __name__== "__main__":
  main()

Или, возможно, вы бы предпочли назначить упакованные данные в качестве атрибута экземпляра _packet, поэтому вызывающей стороне не нужно предоставлять никаких аргументов.

import struct


class _packet:

    def __init__(self, payload):  
        self.version = 1
        self.syn = False
        self.fin = False
        self.reset = False
        self.hasOpt = False
        self.ack = 0
        self.payload = payload

        self.packed_data = None

    def pack(self):
        self.packed_data = struct.pack('????i' + str(len(self.payload)) + 's', self.syn, self.fin, self.reset, self.hasOpt,self.ack, bytes(self.payload, 'utf-8'))
        return self.packed_data

    def unpack(self):
        header_size = 8 #four one-byte bools and one four-byte int
        return struct.unpack('????i' + str(len(packed_data)-header_size) + 's', self.packed_data)

def main():
    packet = _packet("Hello")
    packet.ack = 249
    packet.syn = True
    packet.fin = True
    packet.reset = True
    packedData = packet.pack()
    print(packedData)
    unpackedData = packet.unpack()
    print(unpackedData)

if __name__== "__main__":
  main()

Лично я бы сделал unpack методом класса, поскольку вам не нужно создавать экземпляр _packet для десериализации некоторых байтов в новый объект _packet.Я бы также сделал атрибуты объекта необязательно устанавливаемыми во время инициализации, поэтому вам не нужно присваивать им индивидуально в main.

import struct


class _packet:

    def __init__(self, payload, **kwargs):  
        self.version = 1
        self.syn = kwargs.get("syn", False)
        self.fin = kwargs.get("fin", False)
        self.reset = kwargs.get("reset", False)
        self.hasOpt = kwargs.get("hasOpt", False)
        self.ack = kwargs.get("ack", 0)
        self.payload = payload

    def pack(self):
        return struct.pack('????i' + str(len(self.payload)) + 's', self.syn, self.fin, self.reset, self.hasOpt,self.ack, bytes(self.payload, 'utf-8'))

    #optional: nice string representation of packet for printing purposes
    def __repr__(self):
        return "_packet(payload={}, syn={}, fin={}, reset={}, hasOpt={}, ack={})".format(self.payload, self.syn, self.fin, self.reset, self.hasOpt, self.ack)

    @classmethod
    def unpack(cls, packed_data):
        header_size = 8 #four one-byte bools and one four-byte int
        syn, fin, reset, hasOpt, ack, payload = struct.unpack('????i' + str(len(packed_data)-header_size) + 's', packed_data)
        return cls(payload, syn=syn, fin=fin, reset=reset, hasOpt=hasOpt, ack=ack)
def main():
    packet = _packet("Hello", ack=249, syn=True, fin=True, reset=True)
    packedData = packet.pack()
    print(packedData)
    unpackedData = _packet.unpack(packedData)
    print(unpackedData)

if __name__== "__main__":
  main()
...