[英]Send and receive objects through sockets in Python
I have searched a lot on the Inte.net, but I haven't been able to find the solution to send an object over the socket and receive it as is.我在 Inte.net 上搜索了很多,但一直无法找到通过套接字发送 object 并按原样接收的解决方案。 I know it needs pickling which I have already done.我知道它需要我已经做过的酸洗。 And that converts it to bytes and is received on the other hand.然后将其转换为字节并在另一方面接收。 But how can I convert those bytes to that type of object?但是如何将这些字节转换为 object 的类型呢?
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)
This is the code which is converting the object to StringIO on the client and sending to the server.这是在客户端将 object 转换为 StringIO 并发送到服务器的代码。 And on the server side I am getting bytes.在服务器端,我正在获取字节。 Now I am searching for bytes to be converted to StringIO again so that I can get the object value.现在我正在搜索要再次转换为 StringIO 的字节,以便我可以获得 object 值。
In the code, Object is wrapped in StringIO and is being sent over the socket.在代码中,Object 包装在 StringIO 中并通过套接字发送。 Is there a better approach?有更好的方法吗?
The server-side code is as follows.服务器端代码如下。
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)
The two helper functions are as follows:两个辅助函数如下:
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)
And two of the model classes are as follows:而model类中的两个类如下:
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 you're looking for here is pickle
and the loads
and dumps
operations.您在这里寻找的是pickle
以及loads
和dumps
操作。 Sockets are basically byte streams.套接字基本上是字节流。 Let us consider the case you have.让我们考虑一下您的情况。
class ProcessData:
process_id = 0
project_id = 0
task_id = 0
start_time = 0
end_time = 0
user_id = 0
weekend_id = 0
An instance of this class needs to be pickled into a data string by doing data_string = pickle.dumps(ProcessData())
and unpickled by doing data_variable = pickle.loads(data)
where data
is what is received.此类的一个实例需要通过执行data_string = pickle.dumps(ProcessData())
来腌制为数据字符串,并通过执行data_variable = pickle.loads(data)
来data_variable = pickle.loads(data)
,其中data
是接收到的内容。
So let us consider a case where the client creates an object of ProcessData
and sends it to server.因此,让我们考虑一个客户端创建ProcessData
对象并将其发送到服务器的情况。 Here's what the client would look like.这是客户端的样子。 Here's a minimal example.这是一个最小的例子。
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'
Now your server which receives this data looks as follows现在您接收此数据的服务器如下所示
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'
Running the server first creates a bind
on the port and then running the client
makes the data transfer via the socket.运行服务器首先在端口上创建一个bind
,然后运行client
通过套接字进行数据传输。 You could also look at this answer你也可以看看这个答案
Pickle is not particularly safe for network communications, because it can be used to inject executable code. Pickle 对于网络通信并不是特别安全,因为它可用于注入可执行代码。 I suggest you try json instead.我建议你试试 json 。
pseudocode:伪代码:
import json
to_send = json.dumps(object)
s.sendall (to_send)
An option is to use JSON serialization.一种选择是使用 JSON 序列化。 However Python Object are not serializable, so you have to map your class object into Dict
first, using either function vars
(preferred) or the built-in __dict__
.但是 Python 对象不可序列化,因此您必须首先使用函数vars
(首选)或内置__dict__
将类对象映射到Dict
。
Adapting the answer from Sudheesh Singanamalla and based on this answer :改编来自Sudheesh Singanamalla的答案并基于此答案:
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'
One important point is that dict mapping of an object instance does not map class variable
, only instance variable
.重要的一点是对象实例的字典映射不映射class variable
,只映射instance variable
。 See this anwser for more information.有关更多信息,请参阅此anwser 。 Example:例子:
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
Shameless plug here, but a friend and I have recently released tlspyo , an open-source library whose purpose is to help you transfer python objects over.network easily and in a secure fashion.无耻地插在这里,但我和一个朋友最近发布了tlspyo ,这是一个开源库,其目的是帮助您轻松安全地通过网络传输 python 个对象。
Transferring pickled objects via Inte.net sockets without using something like tlspyo is basically an open door for hackers, so don't do it.通过 Inte.net sockets 传输 pickled 对象而不使用 tlspyo 之类的东西基本上是为黑客敞开的大门,所以不要这样做。
With tlspyo, your code looks like this:使用 tlspyo,您的代码如下所示:
from tlspyo import Relay
if __name__ == "__main__":
my_server = Relay(port=3000,password="<same strong password>")
# (...)
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")
# (...)
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]
# (...)
(You will need to setup TLS for this code to work, check out the documentation - or you can disable TLS using security=None, but if you are transferring over the Inte.net you don't want to do that.) (您需要设置 TLS 才能使此代码正常工作,请查看文档- 或者您可以使用 security=None 禁用 TLS,但如果您通过 Inte.net 进行传输,则不想这样做。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.