Using struct module to send/receive floats over TCP socket with Python

Server

This sends 10 floats at a time, on a loop. The transmit data type is determined by the'=%sf' % payload.sizeformat string passed tostruct.pack.

import socket
from struct import *
import numpy as np


#number of floats we are sending
payload_size = 10
payload = np.linspace(0, 10, payload_size)
print('Sending {}'.format(payload))

#pack the floats as bytes
tx_pack_fmt = '=%sf' % payload.size
packet = pack(tx_pack_fmt, *payload)


HOST = '127.0.0.2'                 
PORT = 50007              
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen(1)
    conn, addr = s.accept()
    with conn:  
        print('Connected by', addr)
        #keep sending until the peer resets the connections
        while True:
            conn.sendall(packet)

output

Sending 
[ 0.          1.11111111  2.22222222  3.33333333  4.44444444  5.55555556
  6.66666667  7.77777778  8.88888889 10.        ]

Client

This receives bytes on a loop. The same format string is used to unpack the bytes.
Note, that struct.unpack expects bytes equal to the size of 10 * floats = 10 * 4.
So we need to keep receiving bytes from socket until we get that number. We tell struct.pack to expect data in the format:{}f'.format(expect_pkt_len)

import socket
from struct import *

#10 floats
expect_pkt_len = 10

#request 4 bytes for int data, use 2 if expecting short, 8 for long.
rx_buffer_size = 4
rx_unpack_fmt = '{}f'.format(expect_pkt_len)

HOST = '127.0.0.2'   
PORT = 50007             
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))

    while True:

        # request bytes from socket stream
        rx_data = s.recv(rx_buffer_size)
        #if not the expected size, request more until we get them
        while len(rx_data) < expect_pkt_len * rx_buffer_size:
            print('Received bytes: {}, waiting for {}'.format(len(rx_data), (expect_pkt_len * rx_buffer_size)))
            rx_data += s.recv(rx_buffer_size)

        data = unpack(rx_unpack_fmt, rx_data)
        print('Received bytes: {}'.format(data))

We can observer how the bytes are received in a stream from the TCP socket by printing out the received byte count for each loop.

output

Received bytes: 4, waiting for 40
Received bytes: 8, waiting for 40
Received bytes: 12, waiting for 40
Received bytes: 16, waiting for 40
Received bytes: 20, waiting for 40
Received bytes: 24, waiting for 40
Received bytes: 28, waiting for 40
Received bytes: 32, waiting for 40
Received bytes: 36, waiting for 40
Received bytes: (0.0, 1.1111111640930176, 2.222222328186035, 3.3333332538604736, 4.44444465637207, 5.55555534362793, 6.666666507720947, 7.777777671813965, 8.88888931274414, 10.0)

Note, if we want to send short we would change the format argument to pack/unpack and set the rx_buffer_size to the number of bytes per number.

If we wanted to mix types, for example in a packet header and payload, we would need to specify the exact header/payload data format inpack/unpack, calculate the exact size of the header/payload that we send/receive and then wait for those number of bytes to be received before trying tounpackthem.

Leave a Reply

Your email address will not be published. Required fields are marked *