使用struct.pack和struct.unpack的问题

时间:2019-03-01 19:58:38

标签: python object struct pack unpack

这是我的代码段:

origin/master

我的目标是创建一个数据包,使用struct.pack对其进行编码并通过套接字发送,然后使用unpack将数据放回元组,以便我可以从中提取必要的位。我的数据包没有一些所需的位,因为这是使用数据包的最小示例。一旦执行该行

master

我收到此作为输出:

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()

这似乎是我所期望的,但是当我运行以下行时就会出现问题:

packedData = packet.pack()
print(packedData)

我收到以下错误:

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

如果我将解压后的数据更改为长度为13的字节数组,则会得到以下输出作为解压后的数据:

 unpackedData = packet.unpack()
 print(unpackedData)

这显然是错误的,因为它不保留我的值,而只是一个不同的数据包实例。

我是否错误地创建了数据包对象?还是我打包和拆包数据不正确?

2 个答案:

答案 0 :(得分:3)

如果您希望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()

答案 1 :(得分:2)

一些注意事项:

  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)