在套接字 Python 中发送 pickled class 对象

Sending pickled class object in socket Python

我正在尝试从客户端向服务器端发送一个 class 对象。我尝试腌制 class 并通过套接字发送腌制字节,但该值未传递到服务器端。

客户端utils.py

from collections import defaultdict


class Utils:
    _filter_ip = []
    _filter_vrf = defaultdict(list)

    @classmethod
    def filter_ip(cls):
        return cls._filter_ip

    @classmethod
    def set_filter_ip(cls, ip_list):
        cls._filter_ip = ip_list

    @classmethod
    def filter_vrf(cls):
        return cls._filter_vrf

    @classmethod
    def set_filter_vrf(cls, device_name, vrf_list):
        cls._filter_vrf[device_name] = vrf_list

客户端client.py

import socket
import pickle
from utils import Utils

HOST = '127.0.0.1'
PORT = 5001
s = socket.socket()
s.connect((HOST, PORT))

Utils.set_filter_ip(['0.0.0.0/0', '10.0.0.0/8'])
Utils.set_filter_vrf('device1', ['vrf1', 'vrf2'])

pickled_data = pickle.dumps(Utils)
s.send(pickled_data)

服务器端utils.py

from collections import defaultdict


class Utils:
    _filter_ip = []
    _filter_vrf = defaultdict(list)

    @classmethod
    def filter_ip(cls):
        return cls._filter_ip

    @classmethod
    def set_filter_ip(cls, ip_list):
        cls._filter_ip = ip_list

    @classmethod
    def filter_vrf(cls):
        return cls._filter_vrf

    @classmethod
    def set_filter_vrf(cls, device_name, vrf_list):
        cls._filter_vrf[device_name] = vrf_list

服务器端server.py

import socket
import os
import json
import pickle
from utils import Utils


class Server:

    _SERVER_HOST = '127.0.0.1'
    _SERVER_PORT = 5001
    _BUFFER_SIZE = 4096
    _SEPARATOR = '<SEPERATOR>'

    def __init__(self):
        self._socket = socket.socket()
        self._client = None

    def init_socket(self):
        self._socket.bind((self._SERVER_HOST, self._SERVER_PORT))
        self._socket.listen(5)

    def listen_socket(self):
        self._client, address = self._socket.accept()

    def recv_data(self):
        data = self._client.recv(self._BUFFER_SIZE)
        pickled_data = pickle.loads(data)
        print(pickled_data.filter_ip())

    def run_server(self):
        self.init_socket()
        self.listen_socket()
        while True:
            self.recv_data()

recv_data 方法打印来自 Utils class 的 unpickled 对象,但 Utils class 中的数据丢失(filter_ip 和 filter_vrf) .需要一些帮助来指出我犯的错误。

阅读文档是个好主意。 this section中解释为:

Note that functions (built-in and user-defined) are pickled by “fully qualified” name reference, not by value. [2] This means that only the function name is pickled, along with the name of the module the function is defined in. Neither the function’s code, nor any of its function attributes are pickled. Thus the defining module must be importable in the unpickling environment, and the module must contain the named object, otherwise an exception will be raised. [3]

Similarly, classes are pickled by named reference, so the same restrictions in the unpickling environment apply.

但是,在 this section 中以及如何允许对 给定 class 进行自定义酸洗的示例可以实现:

import io 
import pickle

class MyClass:
    my_attribute = 1

class MyPickler(pickle.Pickler):
    def reducer_override(self, obj):
        """Custom reducer for MyClass."""
        if getattr(obj, "__name__", None) == "MyClass":
            return type, (obj.__name__, obj.__bases__,
                          {'my_attribute': obj.my_attribute})
        else:
            # For any other object, fallback to usual reduction
            return NotImplemented

f = io.BytesIO() 
p = MyPickler(f) 
p.dump(MyClass)

del MyClass

unpickled_class = pickle.loads(f.getvalue())

assert isinstance(unpickled_class, type) 
assert unpickled_class.__name__ == "MyClass" 
assert unpickled_class.my_attribute == 1

所以在你的情况下,如果真的这么简单,就像:

class MyPickler(pickle.Pickler):
    def reducer_override(self, obj):
        """Custom reducer for Utils."""
        if getattr(obj, "__name__", None) == "Utils":
            return type, (obj.__name__, obj.__bases__, vars(obj))
        else:
            # For any other object, fallback to usual reduction
            return NotImplemented

可以工作。