在Python中通过套接字发送和接收对象

5

我在互联网上搜索了很多,但是没有找到将对象发送到套接字并按原样接收的解决方案。我知道需要使用pickling来完成这个任务,我已经做了。这会将它转换为字节并在另一端接收。但是,我如何将这些字节转换为该类型的对象?

process_time_data = (current_process_start_time, current_process_end_time)
prepared_process_data = self.prepare_data_to_send(process_time_data)
data_string = io.StringIO(prepared_process_data)
data_string =  pack('>I', len(data_string)) + data_string
self.send_to_server(data_string)

这是将对象转换为StringIO并发送到服务器的代码。在服务器端,我得到了字节。现在我正在寻找将字节再次转换为StringIO的方法,以便我可以获取对象值。
在代码中,对象包装在StringIO中并通过套接字发送。有更好的方法吗?
服务器端代码如下。
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#server.setblocking(0)
server.bind(('127.0.0.1', 50000))
server.listen(5)
inputs = [server]
outputs = []
message_queues = {}

while inputs:
    readable, writeable, exceptional = select.select(inputs, outputs, inputs)
    for s in readable:
        if s is server:
            connection, client_address = s.accept()
            print(client_address)
            connection.setblocking(0)
            inputs.append(connection)
            message_queues[connection] = queue.Queue()
            print('server started...')
        else:
            print('Getting data step 1')
            raw_msglen = s.recv(4)
            msglen = unpack('>I', raw_msglen)[0]
            final_data = b''
            while len(final_data) < msglen:
                data = s.recv(msglen - len(final_data))
                if data:
                    #print(data)
                    final_data += data
                    message_queues[s].put(data)
                    if s not in outputs:
                        outputs.append(s)
                    else:
                        if s in outputs:
                            outputs.remove(s)
                else:
                    break
            inputs.remove(connection)
            #s.close()
            del message_queues[s]

            process_data = ProcessData()
            process_screen = ProcessScreen()

            if final_data is not None:
                try:
                    deserialized_data = final_data.decode("utf-8")
                    print(deserialized_data)
                except (EOFError):
                    break
            else:
                print('final data is empty.')

            print(process_data.project_id)
            print(process_data.start_time)
            print(process_data.end_time)
            print(process_data.process_id)

两个辅助函数如下:
def receive_all(server, message_length, message_queues, inputs, outputs):
    # Helper function to recv message_length bytes or return None if EOF is hit
    data = b''
    while len(data) < message_length:
        packet = server.recv(message_length - len(data))
        if not packet:
            return None
        data += packet
        message_queues[server].put(data)
        if server not in outputs:
            outputs.append(server)
        else:
            if server in outputs:
                outputs.remove(server)
    inputs.remove(server)
    del message_queues[server]
    return data


def receive_message(server, message_queues, inputs, outputs):
    # Read message length and unpack it into an integer
    raw_msglen = receive_all(server, 4, message_queues, inputs, outputs)
    if not raw_msglen:
        return None
    message_length = unpack('>I', raw_msglen)[0]

    return receive_all(server, message_length, message_queues, inputs, outputs)

以下是两个模型类:

class ProcessData:
    process_id = 0
    project_id = 0
    task_id = 0
    start_time = 0
    end_time = 0
    user_id = 0
    weekend_id = 0

# Model class to send image data to the server
class ProcessScreen:
    process_id = 0
    image_data = bytearray()

7
请花些时间阅读帮助页面,尤其是名为“在这里我可以问什么话题?”(http://stackoverflow.com/help/on-topic)和“哪些类型的问题应该避免提出?”(http://stackoverflow.com/help/dont-ask)的部分。更重要的是,请阅读[Stack Overflow问题清单](http://meta.stackexchange.com/q/156810/204922)。您可能还想了解[最小完整可验证示例(Minimal, Complete, and Verifiable Examples)](http://stackoverflow.com/help/mcve)。 - Sudheesh Singanamalla
6
“我的老师曾经说过没有什么问题是愚蠢的” - 他们错了,有些问题确实是愚蠢的。这就是其中之一。请查看下面建议的重复内容,了解提问时需要哪些信息,以及可以帮助您得到答案的回答。 - user955340
4
如 @SudheeshSinganamalla 所说,你所提供的问题示例应该是最小化、完整和可验证的。这不是一个完整的示例;它不是一个独立的代码块:也就是说,它不能直接运行。如果没有相应的定义,我们使用的变量和函数的唯一上下文就是它们的名称。这使得回答问题非常困难,更别提充分回答了,这也是为什么它被投票降级的原因:投票的目的是根据优先级对问题进行排名。这可能是一个不好的问题,但它的确提问不清楚。 - Erick Shepherd
2
@Dharmindar,您需要提供更多信息。您可以编辑问题以解决我和其他用户在此处发表的关注点:主要是包括其余示例代码(完整),减去任何与问题不相关的多余块(最小化),并提出一个可验证的单个可重复问题(可验证)。否则,您可能无法得到答案。 - Erick Shepherd
1
你似乎在评论中告诉我要远离。如果你在我发表评论时就将问题编辑成现在的样子,那么你早就收到答案了。我们进行审核的原因是为了让有经验的人回答你的问题,但是当你的问题不清楚或花费太多时间时,没有人会花时间回答它。祝你好运! - Sudheesh Singanamalla
显示剩余7条评论
3个回答

28
你正在寻找pickle和loads、dumps操作。套接字基本上是字节流。让我们考虑一下你的情况。

您正在寻找pickleloadsdumps 操作。套接字基本上是字节流。让我们考虑一下您的情况。

class ProcessData:
    process_id = 0
    project_id = 0
    task_id = 0
    start_time = 0
    end_time = 0
    user_id = 0
    weekend_id = 0

需要将此类的一个实例通过 data_string = pickle.dumps(ProcessData()) 进行序列化为数据字符串,然后可以通过 data_variable = pickle.loads(data) 进行反序列化以获取接收到的 data 数据变量。

考虑客户端创建一个 ProcessData 对象并将其发送到服务器的情况。以下是客户端示例的最简示例。

客户端

import socket, pickle

class ProcessData:
    process_id = 0
    project_id = 0
    task_id = 0
    start_time = 0
    end_time = 0
    user_id = 0
    weekend_id = 0


HOST = 'localhost'
PORT = 50007
# Create a socket connection.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

# Create an instance of ProcessData() to send to server.
variable = ProcessData()
# Pickle the object and send it to the server
data_string = pickle.dumps(variable)
s.send(data_string)

s.close()
print 'Data Sent to Server'

现在接收这些数据的服务器应该如下所示。

服务器

import socket, pickle

class ProcessData:
    process_id = 0
    project_id = 0
    task_id = 0
    start_time = 0
    end_time = 0
    user_id = 0
    weekend_id = 0


HOST = 'localhost'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr

data = conn.recv(4096)
data_variable = pickle.loads(data)
conn.close()
print data_variable
# Access the information by doing data_variable.process_id or data_variable.task_id etc..,
print 'Data received from client'

运行服务器首先在端口上创建一个绑定,然后运行 客户端 通过套接字进行数据传输。你也可以查看这个答案


2
你说你已经运行了它,但是它不起作用。你是如何运行单个文件的?你能打开两个终端,将上面的代码复制到相应的文件server.pyclient.py中。在一个终端中运行服务器,在另一个终端中运行客户端。你会得到一个<__main__.ProcessData instance at 0x10572edd0>作为响应,这是你从客户端发送到服务器的ProcessData类的实例。 - Sudheesh Singanamalla
1
@Dharmindar 这个例子完美地运行,展示并解释了你需要做的事情。这解决了你提出的问题,请将其标记为已接受。 - user955340
2
@Bilkokuya 非常感谢。@Dharmindar 很高兴能帮到你。但是,我想告诉你一些事情。你在上面的评论中提到“你可以回答问题或者说这超出了我的知识范围,而不是回应我的最后一条评论”。请理解我们并没有对你做任何好事,你不应该以这种方式与stackoverflow社区中的任何人交流。祝你工作顺利。 - Sudheesh Singanamalla
1
@SudheeshSinganamalla 我会记住的。再次感谢 :) - Dharmindar
2
问题是由于 4096 字节的限制引起的。您需要一个循环来接收所有数据,然后将4096字节的块连接成您想要的字符串。这里有一个示例。您可以在 stackoverflow 上轻松搜索到这些内容。请不要指望我们写所有东西。 - Sudheesh Singanamalla
显示剩余6条评论

0

这里有一个不要脸的广告,我和一个朋友最近发布了tlspyo,这是一个开源库,旨在帮助您轻松、安全地在网络上传输Python对象。

如果不使用类似tlspyo这样的工具通过Internet套接字传输pickled对象,那么就等于为黑客打开了一扇大门,所以请不要这样做。

使用tlspyo,您的代码看起来像这样:

服务器:

from tlspyo import Relay

if __name__ == "__main__":
    my_server = Relay(port=3000,password="<same strong password>")

    # (...)

客户端1:

from tlspyo import Endpoint

if __name__ == "__main__":
    client_1 = Endpoint(
        ip_server='<ip of your server>'
        port=3000,
        password="<same strong password>",
        groups="client 1")

    # send an object to client 2:
    my_object = "my object"  # doesn't have to be a string, of course
    client_1.broadcast(my_object, "client 2")

    # (...)

客户端 2:

from tlspyo import Endpoint

if __name__ == "__main__":
    client_2 = Endpoint(
        ip_server='<ip of my Relay>'
        port=3000,
        password="<same strong password>",
        groups="client 2")

    # receive the object sent by client 1:
    my_object = client_2.receive_all(blocking=True)[0]


    # (...)

为了让这段代码正常工作,您需要设置TLS,请查看文档 - 或者您可以使用security=None禁用TLS,但如果您正在通过互联网传输数据,则不建议这样做。


-1

一个选择是使用JSON序列化。

然而,Python对象不可序列化,因此您需要先将类对象映射到Dict中,使用vars函数(首选)或内置的__dict__

根据Sudheesh Singanamalla的答案进行调整,基于this answer

客户端

import socket, json

class ProcessData:
    process_id = 0
    project_id = 0
    task_id = 0
    start_time = 0
    end_time = 0
    user_id = 0
    weekend_id = 0


HOST = 'localhost'
PORT = 50007
# Create a socket connection.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

# Create an instance of ProcessData() to send to server.
variable = ProcessData()

# Map your object into dict
data_as_dict = vars(variable)

# Serialize your dict object
data_string = json.dumps(data_as_dict)

# Send this encoded object
s.send(data_string.encode(encoding="utf-8"))

s.close()
print 'Data Sent to Server'

服务器

import socket, json

class ProcessData:
    process_id = 0
    project_id = 0
    task_id = 0
    start_time = 0
    end_time = 0
    user_id = 0
    weekend_id = 0


HOST = 'localhost'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr

data_encoded = conn.recv(4096)
data_string = data_encoded.decode(encoding="utf-8")

data_variable = json.loads(data_string)
# data_variable is a dict representing your sent object

conn.close()
print 'Data received from client'

警告

一个重要的点是,对象实例的字典映射不会映射类变量,只会映射实例变量。请参见this answer以获取更多信息。例如:

class ProcessData:
    # class variables
    process_id = 0
    project_id = 1

    def __init__(self):
        # instance variables
        self.task_id = 2
        self.start_time = 3

obj = ProcessData()
dict_obj = vars(obj)

print(dict_obj)
# outputs: {'task_id': 2, 'start_time': 3}

# To access class variables:
dict_class_variables = vars(ProcessData)

print(dict_class_variables['process_id'])
# outputs: 0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接