Python 结构为网络数据包(未知字节序列)
Python struct as networking data packets (uknown byte sequence)
我正在 Python 中为我在 GameMaker Studio 2 中制作的游戏开发服务器引擎。我目前在制作和发送数据包方面遇到了一些问题。
我已经成功地建立了一个连接并发送了第一个数据包,但是我找不到一个解决方案来发送一个序列中的数据,如果打包结构中的第一个字节等于一个值,然后将其他数据解压缩到给定的序列中。
示例:
types = 'hhh' #(message_id, x, y) example
message_id = 0
x = 200
y = 200
buffer = pack(types, 0,x, y)
在服务器端:
data = conn.recv(BUFFER_SIZE)
mid = unpack('h', data)[0]
if not data: break
if mid == 0:
sequnce = 'hhh'
x = unpack(sequnce, data)[1]
y = unpack(sequnce, data)[2]
你能把整个数据解包到列表中,然后在循环中检查它的元素吗?解压3次的原因是什么?我想,您可以将其解压一次,然后使用该列表 - 首先检查其长度,如果不为空 -> 检查第一个元素 -> 如果等于特殊元素,则继续进行列表解析。你试过了吗?
您的后续解码似乎会根据消息 ID 而有所不同?
如果是这样,您可能希望使用 unpack_from
,它只允许您从数据中提取第一个成员(如现在所写,您最初的 unpack
调用将产生异常,因为您正在处理的缓冲区大小不正确)。然后,您可以使用根据消息 ID 改变解包格式字符串的代码。该代码可能如下所示:
from struct import pack, unpack, unpack_from
while True:
data = conn.recv(BUFFER_SIZE)
# End of file, bail out of loop
if not data: break
mid = unpack_from('!h', data)[0]
if mid == 0:
# Message ID 0
types = '!hhh'
_, x, y = unpack(types, data)
# Process message type 0
...
elif mid == 1:
types = '!hIIq'
_, v, w, z = unpack(types, data)
# Process message type 1
...
elif mid == 2:
...
请注意,在每种情况下,我们都会再次解压缩消息 ID 以及特定于 ID 的参数。如果你愿意,你可以通过使用 unpack_from
:
的可选偏移参数来避免这种情况
x, y = unpack_from('!hh', data, offset=2)
另一个注释说明:如果你在两台不同的机器之间发送消息,你应该考虑"endianness"(字节顺序)。并非所有机器都 "little-endian" 像 x86。因此,通常以特定定义的字节顺序发送整数和其他结构化数字 - 传统上一直是 "network byte order" (这是大端)但只要你保持一致就可以。您可以通过在每个格式字符串前加上 '!' 轻松地做到这一点或如上所示的“<”(您需要为两侧的每个格式字符串执行此操作)。
最后,上面的代码可能适用于简单的 "toy" 应用程序,但随着程序范围和复杂性的增加,您应该意识到不能保证您的单个 recv
调用实际上接收 所有 发送的字节 而没有其他 字节(例如来自随后发送的缓冲区的字节)。换句话说,通常有必要添加一个缓冲层,或者以其他方式确保您已经收到并正在操作您想要的字节数。
我正在 Python 中为我在 GameMaker Studio 2 中制作的游戏开发服务器引擎。我目前在制作和发送数据包方面遇到了一些问题。
我已经成功地建立了一个连接并发送了第一个数据包,但是我找不到一个解决方案来发送一个序列中的数据,如果打包结构中的第一个字节等于一个值,然后将其他数据解压缩到给定的序列中。
示例:
types = 'hhh' #(message_id, x, y) example
message_id = 0
x = 200
y = 200
buffer = pack(types, 0,x, y)
在服务器端:
data = conn.recv(BUFFER_SIZE)
mid = unpack('h', data)[0]
if not data: break
if mid == 0:
sequnce = 'hhh'
x = unpack(sequnce, data)[1]
y = unpack(sequnce, data)[2]
你能把整个数据解包到列表中,然后在循环中检查它的元素吗?解压3次的原因是什么?我想,您可以将其解压一次,然后使用该列表 - 首先检查其长度,如果不为空 -> 检查第一个元素 -> 如果等于特殊元素,则继续进行列表解析。你试过了吗?
您的后续解码似乎会根据消息 ID 而有所不同?
如果是这样,您可能希望使用 unpack_from
,它只允许您从数据中提取第一个成员(如现在所写,您最初的 unpack
调用将产生异常,因为您正在处理的缓冲区大小不正确)。然后,您可以使用根据消息 ID 改变解包格式字符串的代码。该代码可能如下所示:
from struct import pack, unpack, unpack_from
while True:
data = conn.recv(BUFFER_SIZE)
# End of file, bail out of loop
if not data: break
mid = unpack_from('!h', data)[0]
if mid == 0:
# Message ID 0
types = '!hhh'
_, x, y = unpack(types, data)
# Process message type 0
...
elif mid == 1:
types = '!hIIq'
_, v, w, z = unpack(types, data)
# Process message type 1
...
elif mid == 2:
...
请注意,在每种情况下,我们都会再次解压缩消息 ID 以及特定于 ID 的参数。如果你愿意,你可以通过使用 unpack_from
:
x, y = unpack_from('!hh', data, offset=2)
另一个注释说明:如果你在两台不同的机器之间发送消息,你应该考虑"endianness"(字节顺序)。并非所有机器都 "little-endian" 像 x86。因此,通常以特定定义的字节顺序发送整数和其他结构化数字 - 传统上一直是 "network byte order" (这是大端)但只要你保持一致就可以。您可以通过在每个格式字符串前加上 '!' 轻松地做到这一点或如上所示的“<”(您需要为两侧的每个格式字符串执行此操作)。
最后,上面的代码可能适用于简单的 "toy" 应用程序,但随着程序范围和复杂性的增加,您应该意识到不能保证您的单个 recv
调用实际上接收 所有 发送的字节 而没有其他 字节(例如来自随后发送的缓冲区的字节)。换句话说,通常有必要添加一个缓冲层,或者以其他方式确保您已经收到并正在操作您想要的字节数。