【问题标题】:unpack_from requires a buffer of at least 4 bytesunpack_from 需要至少 4 个字节的缓冲区
【发布时间】:2015-07-08 17:24:43
【问题描述】:

我正在接收来自客户端的数据包,其中包含许多字段。我成功读取了所有字段,但是当涉及到最后一个字段 tag_end 时,python 给了我一个错误:

unpack_from 需要至少 4 个字节的缓冲区,但未找到。

这是代码:

def set_bin(self, buf):
    """Reads a vector of bytes (probably received from network or
    read from file) and tries to construct the packet structure
    from it, by reading each packet member from the buffer.  This
    is somehow like deserializing the packet.
    """
    assert isinstance(buf, bytearray), 'buffer type is not valid'
    offset = 0

    print("$$$$$$$$$$$$$$$$ set bin $$$$$$$$$$$$$$$$$")

    try:
        (self._tag_start, self._version, self._checksum, self._connection_id,
         self._packet_seq) = Packet.PACKER_1.unpack_from(str(buf), offset)
    except struct.error as e:
        print(e)
        raise DeserializeError(e)
    except ValueError as e:
        print(e)
        raise DeserializeError(e)
          #I=4 H=2 B=1
    offset = Packet.OFFSET_GUID     #14 correct
    self._guid = buf[offset:offset+Packet.UUID_SIZE] #14-16 correct

    offset = Packet.OFFSET_GUID + Packet.UUID_SIZE

    print("$$$$$$$$$$$$$$$$ GUID read successfully  $$$$$$$$$$$$$$$$$")

    try:
       (self._timestamp_sec, self._timestamp_microsec, self._command,
        self._command_seq, self._subcommand, self._data_seq,
        self._data_length) = Packet.PACKER_3.unpack_from(str(buf), offset)
    except struct.error as e:
        print(e)
        raise DeserializeError(e)
    except ValueError as e:
        print(e)
        raise DeserializeError(e)

    print("$$$$$$$$$$$$$$$$ timestamps read successfully $$$$$$$$$$$$$$$$$")
   offset = Packet.OFFSET_AUTHENTICATE
    self._username = buf[offset:offset + self.USERNAME_SIZE]        #Saman
    offset += self.USERNAME_SIZE

    print("$$$$$$$$$$$$$$$$ username read successfully  $$$$$$$$$$$$$$$$$")

    self._password = buf[offset:offset+self.USERNAME_SIZE]
    offset += self.PASSWORD_SIZE

    print("$$$$$$$$$$$$$$$$ password read successfully $$$$$$$$$$$$$$$$$")

    self._data = buf[offset:offset+self._data_length]
    offset = offset + self._data_length

    print("$$$$$$$$$$$$$$$$ data read successfully  $$$$$$$$$$$$$$$$$")

    try:
        (self._tag_end,) = Packet.PACKER_4.unpack_from(str(buf), offset)
    except struct.error as e:
        print(e)
        raise DeserializeError(e)
    except ValueError as e:
        print(e)
        raise DeserializeError(e)

    print("$$$$$$$$$$$$$$$$ tag end read successfully $$$$$$$$$$$$$$$$$")


    if len(buf) != Packet.PACKER.size + self._data_length:
        print('failed to deserialize binary data correctly and construct the packet due to extra data')
    else:
        print('@@@@@@@@@@@@@@@ Deserialized Successfully')

这是代码中使用的一些常量:

STRUCT_FORMAT_STR   = r'=IHIHH 16B IIHHHHH I 6c 9c' #Saman
STRUCT_FORMAT_STR_1 = r'=IHIHH'
STRUCT_FORMAT_STR_2 = r'=16B'
STRUCT_FORMAT_STR_3 = r'=IIHHHHH'
STRUCT_FORMAT_STR_4 = r'=I'
STRUCT_FORMAT_STR_5 = r'=6c'
STRUCT_FORMAT_STR_6 = r'=9c'
UUID_SIZE   = 16
OFFSET_GUID = 14
#OFFSET_DATA = 48     #shifting offset data by 15 char      
OFFSET_AUTHENTICATE = 48
PACKER   = struct.Struct(str(STRUCT_FORMAT_STR))     #Saman
PACKER_1 = struct.Struct(str(STRUCT_FORMAT_STR_1))
PACKER_2 = struct.Struct(str(STRUCT_FORMAT_STR_2))
PACKER_3 = struct.Struct(str(STRUCT_FORMAT_STR_3))
PACKER_4 = struct.Struct(str(STRUCT_FORMAT_STR_4))
PACKER_5 = struct.Struct(str(STRUCT_FORMAT_STR_5))
PACKER_6 = struct.Struct(str(STRUCT_FORMAT_STR_6))
BYTES_TAG_START = PACKER_4.pack(TAG_START)
BYTES_TAG_END   = PACKER_4.pack(TAG_END)

以及数据包对象的初始化,它在其中初始化字段:

def init(self, **kwargs): 如果 kwargs 中的“buf”: self.set_bin(kwargs['buf']) 别的: 在 Packet.RTCINET_COMMANDS.values() 中断言 kwargs['command'] 和在 Packet.RTCINET_COMMANDS.values() 中断言 kwargs['subcommand'],'未定义的协议命令' 断言 isinstance(kwargs['data'], bytearray), '数据字段的类型无效' 对于(“命令”,“子命令”,“数据”)中的字段: setattr(self, '_' + field, kwargs[field])

    self._tag_start = Packet.TAG_START
    self._version = Packet.VERSION_CURRENT % (Packet.USHRT_MAX + 1)
    self._checksum = Packet.CRC_INIT
    self._connection_id = kwargs.get('connection_id', 0) % (Packet.USHRT_MAX + 1)
    self._packet_seq = Packet.PACKET_SEQ
    Packet.PACKET_SEQ = (Packet.PACKET_SEQ + 1) % (Packet.USHRT_MAX + 1)
    self._guid = uuid.uuid4().bytes
    dt = datetime.datetime.now()
    self._timestamp_sec = int(time.mktime(dt.timetuple()))
    self._timestamp_microsec = dt.microsecond
    # self._command = kwargs['command']
    self._command_seq = kwargs.get('command_seq', 0)
    # self._subcommand = kwargs['subcommand']
    self._data_seq = kwargs.get('data_seq', 0)
    self._data_length = len(kwargs['data'])

    self._username = Packet.USERNAME            #Saman
    self._password = Packet.PASSWORD

我已确保以正确的顺序读取所有字段,因为它是由客户端程序写入数据包中的。但我仍然无法解决这个问题。

你知道如何解决这个问题吗?

【问题讨论】:

  • 嗯...buf 有多长,这个长度是否符合您的预期?
  • 你为什么到处打电话给str(buf)struct 需要 bytesbytearray 或其他缓冲区兼容类型,而您有 bytearray。调用 str 只会将你的缓冲区变成像 "bytearray(b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00@')" 这样的字符串,这显然不会与通常的实际缓冲区具有相同的长度。
  • 我认为开头的assert() 不是一个好主意。除了bytearray 之外,还有其他几种实现缓冲区 API 的类型,因此这可能会拒绝完全有效的值。如果给定值未实现缓冲区 API,则第一个 unpack() 无论如何都会引发相应的异常。

标签: python python-2.x


【解决方案1】:

问题似乎是你无缘无故地将东西转换为str

在某些地方,例如PACKER_1 = struct.Struct(str(STRUCT_FORMAT_STR_1)),它会降低您的代码的可读性和可理解性,但不会影响实际输出。例如,STRUCT_FORMAT_STR_1 已经是str,所以str(STRUCT_FORMAT_STR_1) 是相同的str

但在其他地方,情况远比这糟糕。特别是,查看像Packet.PACKER_1.unpack_from(str(buf), offset) 这样的所有行。在那里,buf 是一个bytearray。 (它必须是,因为你 assert 它。)在 bytearray 上调用 str 会为你提供 bytearray 的字符串表示形式。例如:

>>> b = bytearray(b'abc')
>>> len(b)
3
>>> s = str(b)
>>> s
"bytearray(b'abc')"
>>> len(s)
17

该字符串表示显然通常不会与您所表示的实际缓冲区具有相同的长度。因此,难怪您会收到有关长度错误的错误。 (如果你真的很不走运并且没有任何此类错误,那么你将改为读取垃圾值。)

那么,您应该如何将bytearray 转换为struct 模块可以处理的东西?没有!正如the docs 所说:

几个 struct 函数(和 Struct 的方法)采用缓冲区参数。这是指实现缓冲区协议并提供可读或可读写缓冲区的对象。用于此目的的最常见类型是 bytesbytearray...

【讨论】:

  • 感谢您的清晰解释。不过,由于我是 python 的新手,我并不确切知道哪个代码可以解决问题。你能为我澄清一下吗?因为我已经使用了文档中描述的函数,即 unpack_from。此外,关于您关于返回垃圾值的说明,我注意到此代码到目前为止一直运行良好,没有任何问题。但是,现在我添加了两个新字段:offset = PACKET.OFFSET_AUTHENTICATE; # 48 正确 self._username = buf[offset:offset+self._username_length] #Saman offset += self._username_length
  • 这就是它现在发送错误的原因,如果您的案例有问题,之前没有用户名和密码的代码可能无法正常工作!无论如何,你能向我解释一下吗?
  • @Saman 根本不调用 str() 对您不想从中获取字符串表示的值。这是您要使用unpack()unpack_from() 的所有值。
  • @blackJack:谢谢。但是为什么它以前可以正常工作? !无需添加用户名和密码
  • @Saman:因为你以某种方式破坏了它。大概是通过添加str() 调用。该代码中的第一个unpack() 调用已经是错误的,因为它对bytearray 的字符串表示进行操作。只需看上面答案中的示例即可。
猜你喜欢
  • 1970-01-01
  • 2022-10-24
  • 2021-10-06
  • 2018-04-28
  • 2018-10-06
  • 1970-01-01
  • 2011-07-21
  • 2020-05-11
  • 1970-01-01
相关资源
最近更新 更多