【问题标题】:Python unpickling stack underflowPython unpickling 堆栈下溢
【发布时间】:2014-07-20 18:30:53
【问题描述】:

我一直在开发一个 python 应用程序,其中客户端向服务器发送时钟信号,服务器以音频信号响应。
我有两个按钮,一个用于启动时钟,一个用于暂停曲目。

主类

# function I call when I hit the play button
def play(self):
    start_song = [250]
    global IS_FIRST_PLAY
    if IS_FIRST_PLAY:
        IS_FIRST_PLAY = False
        self.startClock()
    if IS_CONNECTED:
        client.sendMessage(start_song)

# here I start the clock and send a constant clock signal to the client
def startClock(self):
    clock = midi.startClock()
    for i in clock:
        if IS_CONNECTED:
            client.sendMessage(i)
    midi.playing = True

# here I pause the track
def pause(self):
    stop_song = [252]
    if IS_CONNECTED:
        client.sendMessage(stop_song)
    midi.sendMidiMessage(stop_song)
    midi.playing = False
    midi.mtClock = [0, 0, 0, 0]

客户端类

# this is the client.sendMessage() function
def sendMessage(self, message):
    self.s.sendall(pickle.dumps(message))

服务器类

# this is the class that handles the incoming clock signal for the server
class MyTCPHandler(socketserver.BaseRequestHandler):

    def handle(self):
        global IS_FIRST_PLAY, IS_PLAYING      
        thread1 = threading.Thread(target=self.sendAudio)
        thread1.start()
        while True:
            # here throws python an error
            self.data = pickle.loads(self.request.recv(12).strip())

这一切都很好,除了一个随机的时刻,当我改变暂停播放时,我不断收到这个错误:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/socketserver.py", line 306, in _handle_request_noblock
    self.process_request(request, client_address)
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/socketserver.py", line 332, in process_request
    self.finish_request(request, client_address)
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/socketserver.py", line 345, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/socketserver.py", line 666, in __init__
    self.handle()
  File "/Users/cedricgeerinckx/Dropbox/Redux/OSX/Server.py", line 85, in handle
    self.data = pickle.loads(self.request.recv(12).strip())
_pickle.UnpicklingError: unpickling stack underflow

这个问题可能是什么?

【问题讨论】:

  • 看起来你的泡菜坏了。你为什么用recv(12).strip()
  • 我也使用了没有strip()的泡菜,但没有区别。我用send(1024)recv(1024) 发送我的数据,但是发送了错误的数据,因为读取了1024 个字节而不是一行。也许我应该把它改成readline或者什么?

标签: python debugging pickle stackunderflow


【解决方案1】:

unpickling stack underflow 可能会在泡菜意外结束时发生。

这里self.request.recv(12),您最多只能接收 12 个字节,您的腌制对象必须长于 12 个字节,因此它会被切断。

我不建议直接处理 TCP 套接字,除非您非常非常熟悉网络并且需要非常高的性能。我建议使用 HTTP 来包装您的消息并使用 HTTP 库。

如果您必须直接处理 TCP,您将有两种选择:

  1. 您可以在客户端和服务器之间就终止符字符串达成一致,例如“\0”(空)字符;并且您的消息将使用此终止符字符串分隔。终止符字符串绝不能出现在消息正文中(否则您必须想办法在正文中转义终止符字符串);您还需要缓冲数据包,以便在您的读取大小小于或大于对象时接收整个消息,并在终止符字符串上拆分消息。请注意,您还需要处理以下情况:如果连续快速发送多条小消息,接收方可能会在单个.recv() 中收到多条消息。

  2. 也许更简单的方法是将所有消息的长度作为它发送的前四个字节来开始。接收者总是首先从流中读取四个字节,将其解码为一个整数,然后从流中读取那么多字节,这就是一条完整的消息。

或者,如果发送方和接收方都在 Python 中,您可以重新构建程序以使用多处理队列。

我想说,使用 HTTP 库作为您的传输协议可能是最简单的,因为它会处理所有这些详细信息,以便为您分块消息,并且可以跨多台机器和技术使用。

【讨论】:

  • 整数是最大值吗?还是需要准确?因为一开始我用 send(1024) 和 recv(1024) 发送数据,但是发送了错误的数据,因为读取了 1024 个字节而不是一行。
  • @Barto:你传递给 read() 的整数是 read() 将返回的最大字节数, read() 可能 返回的字节数少于请求的字节数,但永远不会更多。
  • 感谢您的解释。一有机会,我就去测试一下!
  • 您还应该知道,对于 TCP 套接字,来自单个 send() 的数据可能会出现在多个 recv() 调用中。 100 个字节的 send() 可能出现在前 25 个字节的 recv() 中,然后是其余 75 个字节的 recv()(或者,可以想象,100 个 recv()s 每个 1 个字节!)。如果必须使用 TCP,长度前缀绝对是必经之路。
猜你喜欢
  • 2014-10-17
  • 2011-01-24
  • 2016-05-06
  • 2019-05-18
  • 1970-01-01
  • 1970-01-01
  • 2012-09-18
  • 2012-11-25
  • 1970-01-01
相关资源
最近更新 更多