如何使用 python 中的 'struct' 库发送 IPv6 地址

How to send an IPv6 address with the 'struct' library in python

我正在寻求你的帮助,因为我已经被同样的问题困扰了 3 天。 如果我有:

Value1 = 0, Value2 = 3.10 and IPv6 = '2001::1'

我想用这个命令打包所有 3 个值:package = struct.pack(*format*, value1, value2, IPv6)

我的问题是:我不知道我可以使用什么 C 类型的格式字符来打包 IPv6 并保留其 16 字节。 我知道我可以将 format = 'i f ?' 与 i 一起用于整数/f 用于浮点数,但我需要找到用什么来替换“?”这是一个IPv6地址的C类型的格式字符来打包三个值。

拜托,有人可以帮助我吗?

为此使用 struct 并不是那么简单,因为在将 then 添加到结构之前,您必须知道 IPv6 地址的正确字节值。 '2001::1' 是一种文本表示形式,它远不能为您提供这些值:您必须在 : 上拆分字符串,用“0”替换缺失值,然后您将得到一个 4要打包到结构中的 16 位数字列表。和它们,当然在您必须考虑的 IPv6 字符串表示中存在极端情况和特殊语法。

幸运的是 Python 已经为您处理好了。 ipaddress stdlib 模块。

只需导入 ipaddress,为包的第一部分格式化结构并将其与 IPv6Address 中的“packed”属性连接 Python 自动为您生成:

import struct
import ipaddress


Value1 = 0
Value2 = 3.10 
IPv6 = '2001::1'

payload = struct.pack("if", (Value1, Value2)) + ipaddress.ip_address(IPv6).packed

但是,我想知道以这种方式简单地打包一个 int 和一个 float 以及一个 IP 地址是否会产生效果 - 无论代码将读取这将是 super 再加上你正在写的代码。 如果您只是将它存储到一个文件中,以便在您的控制下由 Python 程序读回,只需使用 pickle 即可。如果您打算通过网络将这些值发送到非 Python 程序,则使用无模式文本方式来传送它们,例如 JSON,可能会简单得多。

如果你真的想要存储这些,而且只有这些,为了节省space,以紧凑的方式存储,而且有几万个,它们将被同一个程序读回:试试 numpy 数组。它们将处理每个对象类型的紧凑二进制表示,并且可以读取和写入二进制文件,numpy 将为您处理记录偏移量。

我能看到的唯一用例是,如果您在低级协议中有一个不受您控制的程序,而该程序恰好需要这种记录格式。由于您正在猜测如何创建有效负载,并试图将“3.10”作为浮点值进行传达,因此情况似乎并非如此。谈到这一点,“3.10”或其他数字可能无法像这样的结构一样很好地往返作为一个格式良好的 2 位十进制数字值,因为浮点数在内部是如何表示的。我建议你在那里回顾你的目标和需求,不要把事情复杂化。

要解包回来,更简单的方法是使用 struct 仅恢复数值,并将剩余的 16 个字节传递回 ip_address 工厂函数 - 它会自动创建一个 IPv6 对象,该对象的字符串表示是人类友好的 "2001::1".

我在交互式提示中输入“往返”:

In [30]: import struct, ipaddress

In [31]: x = ipaddress.ip_address('2001::1')

In [32]: v1 = 2;v2 = 3.10

In [33]: payload = struct.pack(">if",v1, v2) + x.packed

In [34]: print(payload)
b'\x00\x00\x00\x02@Fff \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'

In [35]: v3, v4, nx = struct.unpack(">if", aa[:-16]) + (ipaddress.ip_address(aa[-16:]),)

In [36]: print(v3, v4, nx)
2 3.0999999046325684 2001::1

需要注意两点:在结构格式化字符串上添加 > 前缀,以确保编码其内容时的字节顺序, 并且如上所述,“3.10”值不会作为“漂亮的两位小数”值往返。如果需要,在服务器端表示数字时使用 round(number, 2)