我正在运行一个非常简单的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的记录(姓名,年龄等)(作为一个名字元组 - 但老实说这不是强制性的。)
非常感谢任何建议或帮助,寻找快速解决方案......
答案 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.send
和Connection.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()
关闭了连接。