Python 在一条消息中通过 UDP 发送字符串和字节

Python Sending String and Bytes over UDP in One Message

在这个程序中,我试图通过 UDP 广播向客户端发送密钥共享。我面临的问题是将字符串和字节编码为一条消息,产生了很多错误。

我想从服务器向客户端发送一个随机 ID 和一个密钥共享,其中包括索引(整数)和共享(16 字节字符串)。我在 id、index 和 share 之间添加了“:”,以便稍后能够在客户端对其进行管理(通过将消息拆分为多个部分)。

我试过将所有内容都转换成字符串,例如: message = (str(id) + ":" + str(index) + ":").encode('utf-16') + str(share, 'utf-16')。但这会导致客户端的密钥重建出现问题,其中密钥共享应该是一个字节字符串,看起来像 b"b'\xff\xfej\xb9\xdb\x8c&\x7f\x06\x87\x98Ig\xfc\x1eJ\xf6\xb5'".

然后我尝试将 id 和索引编码为 utf-16 并向客户端发送消息,然后对其进行解码,但这并不能让我重建密钥并且出现错误:ValueError: The encoded value must be an integer or a 16 byte string.

当我在客户端解码时,数据是这样的:f91f7e52-865d-49bc-bb45-ad80255e9ef9:5:륪賛缦蜆䦘ﱧ䨞뗶。但是有些share解码后没有分界符,无法拆分。

有没有办法让服务器在一条消息中发送以下所有数据(id、索引、共享),以便在客户端正确分离?

服务器的期望输出是(id = string,index = int,share = 16-byte string):

id = f91f7e52-865d-49bc-bb45-ad80255e9ef9
index = 5 
share = b'\xff\xfej\xb9\xdb\x8c&\x7f\x06\x87\x98Ig\xfc\x1eJ\xf6\xb5'

Server.py

from socket import * 
import socket
import time
import uuid
from Crypto.Random import get_random_bytes
from Crypto.Protocol.SecretSharing import Shamir

server = socket.socket(AF_INET, SOCK_DGRAM)
server.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
server.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)

id = uuid.uuid4()

# generate 16-byte key
key = get_random_bytes(16)
# prepare k chunks of key
shares = Shamir.split(3, 5, key)

while True:
    for index, share in shares:
        message = (str(id) + ":" + str(index) + ":").encode('utf-16') + share
        # send bytes 
        server.sendto(message, ('<broadcast>', 37020))
        print("message sent!")
        time.sleep(1)

Client.py

from socket import * 
from Crypto.Protocol.SecretSharing import Shamir

audienceSocket = socket(AF_INET, SOCK_DGRAM)
audienceSocket.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
audienceSocket.bind(('', 37020))

receivedShares = {}
reconstruct = []

while True:
    # get data from sender
    data, address = audienceSocket.recvfrom(1024)
    
    decoded = data.decode('utf-16')
    print("Decoded:", decoded)

    id, index = decoded.split(":", 1)
    index, share = index.split(":", 1)
    share = share.encode('utf-16')

    print(id)
    print(index)
    print(share)

    if id not in receivedShares:
        receivedShares[id] = set() 
    receivedShares[id].add((index, share))
    print(receivedShares)
    
    for shares in receivedShares:
        if len(shares) >= 3: 
            for share in shares:
                reconstruct.append(share)
            # reconstrcut the key  
            key = Shamir.combine(reconstruct)
            print("The key is:", key)

UUID可以编码为16字节的值,整数例如为 4 个字节,并且 share 的长度为 16 个字节。因此,数据可以简单地连接而无需定界符并按其长度分隔,例如:

import uuid
from Crypto.Random import get_random_bytes
from Crypto.Protocol.SecretSharing import Shamir

id_sender = uuid.uuid4()
key = get_random_bytes(16)
shares = Shamir.split(3, 5, key)

for index_sender, share_sender in shares:
    
    # Encode
    message = id_sender.bytes + index_sender.to_bytes(4, byteorder='big') + share_sender 

    # Send message via socket

    # Decode  
    id_receiver = uuid.UUID(bytes=message[:16])
    index_receiver = int.from_bytes(message[16:20], byteorder='big')
    share_receiver = message[20:]

    print(id_sender == id_receiver, index_sender == index_receiver, share_sender == share_receiver) # True True True