[英]Send and receive objects through sockets in Python
我在 Inte.net 上搜索了很多,但一直無法找到通過套接字發送 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)
這是在客戶端將 object 轉換為 StringIO 並發送到服務器的代碼。 在服務器端,我正在獲取字節。 現在我正在搜索要再次轉換為 StringIO 的字節,以便我可以獲得 object 值。
在代碼中,Object 包裝在 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)
而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()
您在這里尋找的是pickle
以及loads
和dumps
操作。 套接字基本上是字節流。 讓我們考慮一下您的情況。
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_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'
運行服務器首先在端口上創建一個bind
,然后運行client
通過套接字進行數據傳輸。 你也可以看看這個答案
Pickle 對於網絡通信並不是特別安全,因為它可用於注入可執行代碼。 我建議你試試 json 。
偽代碼:
import json
to_send = json.dumps(object)
s.sendall (to_send)
一種選擇是使用 JSON 序列化。 但是 Python 對象不可序列化,因此您必須首先使用函數vars
(首選)或內置__dict__
將類對象映射到Dict
。
改編來自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'
重要的一點是對象實例的字典映射不映射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
無恥地插在這里,但我和一個朋友最近發布了tlspyo ,這是一個開源庫,其目的是幫助您輕松安全地通過網絡傳輸 python 個對象。
通過 Inte.net sockets 傳輸 pickled 對象而不使用 tlspyo 之類的東西基本上是為黑客敞開的大門,所以不要這樣做。
使用 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]
# (...)
(您需要設置 TLS 才能使此代碼正常工作,請查看文檔- 或者您可以使用 security=None 禁用 TLS,但如果您通過 Inte.net 進行傳輸,則不想這樣做。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.