【问题标题】:Python send and receive objects through SocketsPython 通过 Sockets 发送和接收对象
【发布时间】:2018-05-03 15:47:35
【问题描述】:

我在网上进行了很多搜索,但无法找到通过套接字发送对象并按原样接收的解决方案。我知道它需要我已经做过的酸洗。并将其转换为字节并在另一方面被接收。但是如何将这些字节转换为那种类型的对象。

        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()

【问题讨论】:

  • 请花一些时间阅读帮助页面,尤其是名为"What topics can I ask about here?""What types of questions should I avoid asking?" 的部分。更重要的是,请阅读the Stack Overflow question checklist。您可能还想了解Minimal, Complete, and Verifiable Examples
  • @Dharmindar “一旦我的老师说没有问题是不好的” - 他们错了,肯定有不好的问题。这是其中之一。请参阅下面的建议副本,了解提问时需要哪些信息,以及对您有帮助的答案。
  • 正如@SudheeshSinganamalla 所说,您的问题示例应该是最小的、完整的和可验证的。这不是一个完整的例子;它不是一个独立的代码块:也就是说,它不能按原样运行。如果没有各自的定义,我们所使用的变量和函数的唯一上下文就是它们的名称。这使得回答这个问题变得非常困难,更不用说充分回答了,这就是它被否决的原因:投票的目的是一种根据优先级对问题进行排序的分类。这可能是一个坏问题,也可能不是一个坏问题,但肯定问得不好。
  • @Dharmindar 你需要给我们更多的合作。您可以编辑问题以解决我和其他在这里评论的用户表达的担忧:主要包括示例代码的其余部分(complete),减去与问题无关的任何多余块( minimal)具有一个我们可以解决的可重复问题(verifiable)。否则,你不可能得到答案。
  • 你似乎在让我远离的地方制造了cmets。如果您在我发表评论时已将问题编辑为远程查看,那么您会同时收到答案。我们调整的原因是让有经验的人可以回答您的问题,但是当您的问题不清楚或占用太多时间时,没有人会花时间回答它。祝你好运!

标签: python sockets


【解决方案1】:

您在这里寻找的是pickle 以及loadsdumps 操作。套接字基本上是字节流。让我们考虑一下您的情况。

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())被pickle成一个数据字符串,并通过data_variable = pickle.loads(data)被unpickled,其中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'

运行服务器首先在端口上创建一个bind,然后运行client,使数据通过套接字传输。你也可以看看this answer

【讨论】:

  • 你是什么意思你已经运行它并且它不起作用。您如何运行各个文件?你能不能打开两个终端,把上面的代码复制到各自的文件server.pyclient.py中。在一个终端中运行服务器,在另一个终端中运行客户端。您将收到 &lt;__main__.ProcessData instance at 0x10572edd0&gt; 作为响应,它是您从客户端发送到服务器的 ProcessData 类的实例。
  • @Dharmindar 这个例子完美地展示并解释了你需要完美地做些什么。这解决了您提出的问题,请将其标记为已接受。
  • @Bilkokuya 非常感谢。 @Dharmindar 很高兴它有所帮助。不过,我想告诉你一件事。您在Instead responding to my last comment you could have answered the question or could have said that it is out of my knowledge 上面的 cmets 中提到。请理解我们不是在帮您一个忙,您不应该以这种方式与 stackoverflow 社区中的任何人交谈。祝你工作顺利。
  • @SudheeshSinganamalla 会牢记这一点。再次感谢:)
  • 问题是由于4096 字节的限制。您需要一个循环来接收所有数据,然后将join 4096 字节块分成您想要的字符串。 Here 就是一个例子。您可以在 stackoverflow 上轻松搜索这些内容。请不要指望我们会写所有东西。
【解决方案2】:

Pickle 对于网络通信并不是特别安全,因为它可以用来注入可执行代码。我建议你改用 json。

伪代码:

import json
to_send  = json.dumps(object)
s.sendall (to_send)

【讨论】:

  • 类对象不是 JSON可序列化的!
【解决方案3】:

一个选项是使用 JSON 序列化。 但是 Python 对象不可序列化,因此您必须首先使用函数 vars(首选)或内置的 __dict__ 将类对象映射到 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'

警告

重要的一点是对象实例的dict映射不映射class variable,只映射instance variable。有关更多信息,请参阅此anwser。示例:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-12
    • 1970-01-01
    • 2015-07-12
    • 2019-07-10
    • 2020-06-08
    • 2014-09-30
    • 2011-08-06
    • 1970-01-01
    相关资源
    最近更新 更多