Python 解压栈下溢

16 投票
1 回答
54411 浏览
提问于 2025-04-18 08:06

我在做一个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

这个问题可能是什么呢?

1 个回答

12

unpickling stack underflow 这个问题发生在你用的“pickle”数据格式意外中断的时候。

在这里,self.request.recv(12) 表示你最多只能接收12个字节的数据,而你的“pickle”对象必须比12个字节长,所以数据就被截断了。

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

不过,如果你确实需要直接处理TCP,有两种选择:

  1. 你可以和客户端及服务器之间约定一个结束符,比如说'\0'(空字符);这样你的消息就可以用这个结束符来分隔。这个结束符在消息内容中不能出现(否则你得想办法处理结束符在内容中的情况);你还需要缓存你的数据包,以便在读取的字节数小于或大于你的对象时,能够接收到完整的消息,并根据结束符来分割消息。注意,如果多个小消息快速发送,接收方可能会在一次.recv()中接收到多个消息,这种情况也需要处理。

  2. 另一种可能更简单的方法是让所有消息的开头先发送它的长度,作为前四个字节。接收方总是先从流中读取四个字节,把它解码成一个整数,然后再从流中读取那么多字节,这样就能得到一条完整的消息。

另外,如果发送方和接收方都是用Python写的,你可以考虑重新设计程序,使用多进程队列来处理。

我认为使用HTTP库作为传输协议可能是最简单的选择,因为它会为你处理消息分块的所有细节,并且可以在多台机器和不同技术之间使用。

撰写回答