杰瑞科技汇

python udp unpack

Of course! Unpacking data in a Python UDP socket is a fundamental task. The process involves two main steps:

  1. Receiving the Datagram: Use socket.recvfrom() to get the raw byte string and the sender's address.
  2. Unpacking the Bytes: Convert the byte string into a usable format. This is typically done using the struct module for fixed-size binary data or by decoding text/JSON.

Let's break down the most common methods, from simple to more robust.


The Core Concept: socket.recvfrom()

When you receive data over UDP, you don't just get the data. You get a tuple containing:

  1. data: The raw data as a bytes object.
  2. addr: A tuple containing the sender's IP address and port number, e.g., ('192.168.1.100', 54321).
import socket
# Setup a socket (this will be shown in full examples below)
# s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Receive up to 1024 bytes
# data, addr = s.recvfrom(1024)
print(f"Received data: {data}")
print(f"Data type: {type(data)}") # This will be <class 'bytes'>
print(f"From address: {addr}")

The data variable is what you need to "unpack".


Method 1: Unpacking Text (Simple and Common)

If your application sends simple text, the "unpacking" is just decoding the bytes into a string.

Sending Side:

# sender.py
import socket
msg = b"Hello from the sender!" # Must be bytes
addr = ("127.0.0.1", 12345)     # (IP, Port)
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    s.sendto(msg, addr)
    print("Sent text message.")

Receiving Side (The "Unpacking"):

# receiver.py
import socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    s.bind(("127.0.0.1", 12345))
    print("Waiting for data...")
    # 1. Receive the datagram
    data, addr = s.recvfrom(1024)
    # 2. Unpack the bytes into a string
    text_message = data.decode('utf-8') # Or 'ascii', 'latin-1', etc.
    print(f"Received: '{text_message}' from {addr}")

Output on Receiver:

Waiting for data...
Received: 'Hello from the sender!' from ('127.0.0.1', 54321) # Port will vary

Method 2: Unpacking with struct (For Binary Data)

This is the most powerful and common method for handling fixed-size binary data (e.g., sensor readings, network packets). The struct module lets you pack and unpack C-style data structures.

The format string uses characters to represent data types:

  • h: short (e.g., 2 bytes)
  • i: int (e.g., 4 bytes)
  • f: float (e.g., 4 bytes)
  • d: double (e.g., 8 bytes)
  • c: char (1 byte)
  • s: bytes (string)

Sending Side: Let's send a packet with a sensor ID (short), a temperature (float), and a humidity (float).

# sender_struct.py
import socket
import struct
# Data to send
sensor_id = 101
temperature = 23.5
humidity = 45.2
# Pack the data into a binary format
# Format: < (little-endian) h (short) f (float) f (float)
packed_data = struct.pack('<hff', sensor_id, temperature, humidity)
addr = ("127.0.0.1", 12346)
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    s.sendto(packed_data, addr)
    print("Sent packed binary data.")

Receiving Side (The "Unpacking"): Here we use struct.unpack() with the exact same format string to reverse the process.

# receiver_struct.py
import socket
import struct
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    s.bind(("127.0.0.1", 12346))
    print("Waiting for binary data...")
    # 1. Receive the datagram
    data, addr = s.recvfrom(1024)
    # 2. Unpack the bytes using struct
    # The format string MUST match the sender's.
    # We get a tuple back.
    unpacked_tuple = struct.unpack('<hff', data)
    # 3. Assign the values to variables for clarity
    sensor_id, temperature, humidity = unpacked_tuple
    print(f"Received from {addr}:")
    print(f"  Sensor ID: {sensor_id}")
    print(f"  Temperature: {temperature}°C")
    print(f"  Humidity: {humidity}%")

Output on Receiver:

Waiting for binary data...
Received from ('127.0.0.1', 54321):
  Sensor ID: 101
  Temperature: 23.5°C
  Humidity: 45.2%

Method 3: Unpacking JSON (For Complex, Self-Describing Data)

If your data has a complex structure or needs to be human-readable, JSON is an excellent choice. You serialize (dump) a Python dictionary to a JSON string, encode it to bytes, send it, and then do the reverse on the other side.

Sending Side:

# sender_json.py
import socket
import json
# Data to send (a Python dictionary)
data_dict = {
    "sensor_id": 202,
    "location": "Server Room",
    "readings": {
        "temperature": 19.1,
        "humidity": 60.5,
        "status": "OK"
    }
}
# 1. Serialize dict to a JSON string
json_string = json.dumps(data_dict)
# 2. Encode the string to bytes
json_bytes = json_string.encode('utf-8')
addr = ("127.0.0.1", 12347)
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    s.sendto(json_bytes, addr)
    print("Sent JSON data.")

Receiving Side (The "Unpacking"):

# receiver_json.py
import socket
import json
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
    s.bind(("127.0.0.1", 12347))
    print("Waiting for JSON data...")
    # 1. Receive the datagram
    data, addr = s.recvfrom(1024)
    # 2. Decode the bytes back into a JSON string
    json_string = data.decode('utf-8')
    # 3. Parse the JSON string back into a Python dictionary
    received_dict = json.loads(json_string)
    print(f"Received from {addr}:")
    print(received_dict)
    print(f"Parsed Temperature: {received_dict['readings']['temperature']}°C")

Output on Receiver:

Waiting for JSON data...
Received from ('127.0.0.1', 54321):
{'sensor_id': 202, 'location': 'Server Room', 'readings': {'temperature': 19.1, 'humidity': 60.5, 'status': 'OK'}}
Parsed Temperature: 19.1°C

Summary and Best Practices

Method Best For Pros Cons
Text Simple messages, logging, human-readable protocols. Very simple, no extra libraries needed. Not efficient for binary data; prone to encoding issues.
struct Binary data, performance-critical applications, fixed-size packets. Highly efficient, compact, fast. Brittle; format string must match exactly on both sides. Hard to debug.
JSON Complex, nested data, self-describing protocols, APIs. Human-readable, flexible, handles complex data structures easily. Larger packet size, slower than binary due to text overhead.

Key Considerations:

  1. Endianness: Notice the < in the struct format string (<hff). This means "little-endian". If you send data between machines with different CPU architectures (e.g., Intel x86 and some ARM chips), you must specify the endianness explicitly to avoid data corruption. Common choices are < for little-endian and > for big-endian.
  2. Buffer Size: s.recvfrom(1024) means you are allocating a buffer of 1024 bytes. If a packet larger than 1024 bytes is sent, it will be truncated (the excess will be lost). Choose a buffer size large enough for your largest possible packet.
  3. Error Handling: UDP is "unreliable." Packets can be lost or arrive out of order. For robust applications, you need to implement your own error checking, like sequence numbers or acknowledgements (ACKs), on top of UDP.
分享:
扫描分享到社交APP
上一篇
下一篇