在 Python 中创建应用程序级数据包

Creating application level packets in Python

我有一个基本的多线程客户端服务器 运行 使用 python 3.6

现在,一旦建立连接,我想创建应用程序级数据包,这些数据包将通过 tcp/ip 发送。这些的目的是进行三次握手以识别多个客户端然后对它们进行身份验证。该数据包还将用于将某些有效负载发送到服务器。

由于 python 没有任何数据类型,例如结构,所以我很难创建这些 packets.I 不能使用元组,因为它们是不可变的,我尝试使用记录类和结构来自 c_type.

使用 recordclass 时数据发送不正确,因为我不知道每个数据包的确切大小,我将 recv() 参数保持在最大限制但是如果数据包的长度,这会使客户端进入阻塞状态比最大限制短。 在使用 c_type 结构时,我可以发送数据,但它是以这样的格式接收的 \xbct\x00\x106\xe0\x02ff\xc8B 并且我无法将其转换回原始格式。

我们将不胜感激任何形式的帮助。

编辑: 到目前为止,我已经做到了。我在下面附上了我正在使用的代码片段,结构字段是任意的,我稍后会更改它们。

服务器端:

...
   ...
   class app_packet(Structure):
       _fields_ = [('packet_type',c_wchar_p),
                  ('sensor_name',c_wchar_p),
                  ('value',c_float)]

   syn=app_packet('syn','temperature',100.2)
   connectionSocket.sendall(syn)
   ...
   ...

客户端:

    ...
    ...
    class app_packet(Structure):
        _fields_ = [('packet_type',c_wchar_p),
                    ('sensor_name',c_wchar_p),
                    ('value',c_float)]

    data=clientSocket.recv(1024)
    syn=unpack('3s4sf',data)
    a=str(syn)
    print("unpacked="+a)
    ...
    ...

但问题仍然存在,即使在我解压缩接收到的数据包后,字符串数据仍为字节格式,而浮点数据已正确转换。这就是我从 print 语句

的输出中得到的
unpacked=  (b'(\xbcY', b'\x00\xa0\xa6\xc2', 100.19999694824219)

我尝试了不同的 encoding/decoding 方案,但到目前为止没有任何效果,我无法将其转换回来

您的代码的主要问题是您实际上并未发送字符串。

您已经定义了一个包含两个 c_wchar_p 成员的结构,或者用 C 语言来说,是 wchar_t * 指针。您发送这些指针,但从不发送它们指向的数据。这不可能行得通。

您不能只发送包含任何语言指针的 C structs。您必须编写一些 higher-level 协议,以某种方式包含实际字符串而不是指针,然后编写序列化和反序列化到 struct 的代码。使用像 struct.pack 这样的函数比使用 ctypes.Structure 更容易。在像 netstrings 这样的 higher-level 协议之上做起来更容易,如果你只使用带有一些 human-readable 框架的 text-based 协议就更容易了,比如 newline-escaped JSON 以换行符作为分隔符的文本。


如果您真的想要一个基于仅将 fixed-sized 结构转储到网络的二进制协议,您的结构必须是 fixed-sized 和 self-contained。例如,如果您的 packet_type 始终最多 4 个字符,而您的 sensor_name 最多 30 个字符,并且可以接受将 space 浪费在较短的名称上,您可以这样做:

class app_packet(Structure):
    _fields_ = [('packet_type',c_wchar*4),
                ('sensor_name',c_wchar*30),
                ('value',c_float)]

现在字符直接嵌入到结构中,这样就可以了。


除了它不会真正起作用,因为您的数据类型不是 network-portable。 wchar_t 可以是 2 个字节或 4 个字节——不仅在不同平台之间,甚至在同一平台上使用不同编译器或标志构建的二进制文件之间也是如此。 (另外,它们当然是 native-endian。)如果你真的想要嵌入 2 字节或 4 字节的字符串,你必须明确说明:使用 c_uint16c_uint32,用 s.encode('utf-16')s.encode('utf-32') 编码,然后 memcpycast 和 slice-copy。但是当然,在您将它们拉出,将它们放回原处并对其进行解码之前,它们当然不是您代码中的字符串,此时您不妨首先使用适当的协议。


此外,目前还不清楚为什么您首先希望握手数据存储在这样的结构中。为什么不把它作为一个带有两个字符串和一个浮点数的元组(或 namedtuple 或正常的 class)来传递,并且 serialize/deserialize 就在它从网络进入 over/comes 的时候.

您在评论中提到您需要它们是可变的,但这并不能解释它;您想要可变的握手数据就更没有意义了。此外,你可以简单地用不同的字符串创建一个新的元组,而不是让 string-like 成员可以就地改变。