【问题标题】:Parsing packets from TCP stream从 TCP 流中解析数据包
【发布时间】:2014-02-21 22:40:18
【问题描述】:

我经常编写简单的python TCP 服务器,它在解析一个以长度为前缀的数据包后响应请求。假设已设置套接字,这通常看起来很像这样:

def tcp_server_loop():
    msg = ''
    msg_len = 0
    while True:
        msg += sock.recv(4096)
        if len(msg) >= 4 and msg_len == 0:
            msg_len, = struct.unpack_from("!I", msg)
        if len(msg) >= msg_len:
            protocol.parse_packet(msg[:msg_len])
            msg = msg[msg_len:]
            msg_len = 0

这很有效,并且多次为我服务,但我一直对附加在msg += sock.recv(4096) 中的字符串感到厌烦。对于小数据包,这还不错,因为为这些小字符串分配新存储的开销也不错。但是对于大数据包 (MB),Python 的字符串实现在幕后进行了大量的复制。

在 C 或一些类似的语言中,环形缓冲区是明显的数据结构,其大小适合您期望的最大数据包。但是,我还没有找到类似的 Python 实现。我想知道是否有人可以改进我上面的代码。您如何实现这些类型的服务器?

【问题讨论】:

    标签: python tcp stream packet


    【解决方案1】:

    首先给出一个快速建议:为了清楚起见,您可能希望将 packet_size 重命名为 msg_len。您试图从 TCP 流中解析出的是应用程序级协议消息,而不是 TCP 段(又名 TCP 数据包)。

    但是要解决您的问题:一种更有效的方法是,当您收到消息头时,分配第二个固定大小的bytearray 长度为msg_len 的缓冲区。使用它来存储您随后读入的数据。

    【讨论】:

    • 变量名取点,更新了原代码,更清晰。
    • 我不确定我是否理解创建字节数组如何提高效率。这不需要msg 和字节数组之间的副本吗?当前的实现只将一个切片传递给协议解析器,我相信它不需要副本。
    • 是的,但我认为当您追加到msg 时,热点是内存的重新分配,而不是副本,只要您sock.recv() 的字节数不大。如果你也将msg 变成bytearray,你可能会因为不需要解释器进行类型转换而赢得一些额外的东西。
    猜你喜欢
    • 1970-01-01
    • 2012-12-21
    • 2015-02-02
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-17
    • 2016-04-19
    相关资源
    最近更新 更多