Pickle EOFError:从套接字中恢复时退出输入

时间:2014-07-13 19:43:21

标签: python sockets pickle

我正在运行一个非常简单的python(3.x)客户端 - 服务器程序(在我的PC本地)用于学校项目(不适用于现实世界),它只是来回发送消息(如视图)客户,添加客户,删除客户等...真正的基本)。

有时数据可以是多条记录,我将它们存储为namedTuples(只是有意义)然后沿着使用Pickle进行传输的路径。

因此,例如在客户端我做这样的事情:

s.send(message.encode('utf-8'))
pickledResponse = s.recv(4096);
response = pickle.loads(pickledResponse)

现在我经常遇到以下错误:

response = pickle.loads(pickledResponse)
EOFError: Ran out of input

我担心这与我的套接字(TCP)传输有关,也许某种程度上我没有得到所有数据及时的pickle.loads - 有意义吗?如果不是,我真的迷失了为什么这会发生如此不一致。

然而,即使我是对的,我也不确定如何解决它(很快),我正在考虑放弃泡菜而只是使用字符串(但这不会遭受同样的命运)?有没有人有任何建议?

真的我的信息非常基本 - 通常只是一个命令和一些小数据,如" 1 = John"这意味着命令(1)是FIND命令然后" John"并且它返回John的记录(姓名,年龄等)(作为一个名字元组 - 但老实说这不是强制性的。)

非常感谢任何建议或帮助,寻找快速解决方案......

2 个答案:

答案 0 :(得分:5)

您的代码存在的问题是,recv(4096)在TCP套接字上使用时,可能会返回与您预期的数据不同的数据,因为它们是在数据包边界处切割的。

简单的解决方案是为每条消息添加长度;发送像

import struct
packet = pickle.dumps(foo)
length = struct.pack('!I', len(packet)
packet = length + packet

然后接收

import struct

buf = b''
while len(buf) < 4:
    buf += socket.recv(4 - len(buf))

length = struct.unpack('!I', buf)[0]
# now recv until at least length bytes are received,
# then slice length first bytes and decode.

但是,Python标准库已经支持面向消息的pickle socket,即multiprocessing.Connection,它支持分别使用Connection.sendConnection.recv轻松发送和接收pickle。

因此,您可以将服务器编码为

from multiprocessing.connection import Listener

PORT = 1234
server_sock = Listener(('localhost', PORT))
conn = server_sock.accept()

unpickled_data = conn.recv()

和客户端

from multiprocessing.connection import Client

client = Client(('localhost', 1234))
client.send(['hello', 'world'])

答案 1 :(得分:0)

要接收服务器发送的所有内容,直到它关闭其连接端,请尝试:

import json
import socket
from functools import partial


def main():
    message = 'Test'

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect(('127.0.0.1', 9999))

        sock.sendall(message.encode('utf-8'))
        sock.shutdown(socket.SHUT_WR)

        json_response = b''.join(iter(partial(sock.recv, 4096), b''))

    response = json.loads(json_response.decode('utf-8'))
    print(response)


if __name__ == '__main__':
    main()

我使用了sendall(),因为send()recv()具有相同的“问题”:不保证所有内容都已发送。 send()返回实际发送的字节数,程序员必须确保匹配参数的长度,如果没有,则发送其余的直到一切都结束。发送连接的写入端关闭后(shutdown()),以便服务器知道没有更多来自客户端的数据。之后,接收来自服务器的所有数据,直到服务器关闭其连接端,从而导致从recv()调用返回的空字节对象。

以下是客户的合适socketserver.TCPServer

import json
from socketserver import StreamRequestHandler, TCPServer


class Handler(StreamRequestHandler):

    def handle(self):
        print('Handle request...')
        message = self.rfile.read().decode('utf-8')
        print('Received message:', message)
        self.wfile.write(
            json.dumps(
                {'name': 'John', 'age': 42, 'message': message}
            ).encode('utf-8')
        )
        print('Finished request.')



def main():
    address = ('127.0.0.1', 9999)
    try:
        print('Start server at', address, '...')
        server = TCPServer(address, Handler)
        server.serve_forever()
    except KeyboardInterrupt:
        print('Stopping server...')


if __name__ == '__main__':
    main()

它从客户端读取完整数据,并将其放入带有其他一些固定项的JSON编码响应中。它不是使用低级套接字操作,而是使用更方便的文件,如TCPServer提供的用于从/向连接读取和写入的对象。在TCPServer方法完成后,handle()关闭了连接。