August 2, 2023 · Andrew Hyde

Simple C UDP Broadcast Client

How to write a simple C UDP broadcast test program.

cudpnetworking

Recently I had a project that required graphing several values from a motor control PID loop. The PID loop was running on embedded hardware in a C process. This is the first part of several describing the general approach taken. The examples provided in this and the next posts are for example purposes and not robust enough for production.

All the development and testing has been done on linux.

Sample Application

The sample application can be found here.

User Datagram Protocol

The User Datagram Protocol (UDP) is a lightweight connectionless messaging protocol. It sits like TCP above IP in the protocol stack. Unlike TCP, it has no guarantee that packets will arrive or be in order.

IP Broadcast

An IP broadcast is a packet sent to everyone on the originating subnet. At the IP layer you send a broadcast by sending packets to a specific address.

According to wikipedia:

The broadcast address for any IPv4 host can be obtained by taking the bit complement
 (bitwise NOT) of the subnet mask and then performing a bitwise OR operation with the
 host's IP address. A shortcut to this process (for common masks using only 0 and 1
 bit placements) is to simply take the host's IP address and set all bits in the host
 identifier portion of the address (any bit positions which hold a 0 in the subnet
 mask) to 1."

I am going to be broadcasting from IP address 192.168.0.216 with a subnet mask of 255.255.255.0. I will use the broadcast address of 192.168.0.255.

The Client Program

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <netdb.h>
#include <math.h>
#include <time.h>

static const int BROADCAST_PORT = 9999;
static const char BROADCAST_IP[] = "192.168.0.255";
static const int RATE_MS = 50000000;

static int open_broadcast_socket (int *broadcast_socket, struct sockaddr_in *host_addr){

    int trueval = 1;

    memset(host_addr, 0, sizeof(struct sockaddr_in));
    host_addr->sin_family = AF_INET;
    host_addr->sin_addr.s_addr = inet_addr(BROADCAST_IP);
    host_addr->sin_port = htons (BROADCAST_PORT);

    *broadcast_socket = socket (PF_INET, SOCK_DGRAM, getprotobyname ("udp")->p_proto);
    if (broadcast_socket < 0){
        printf("Error creating broadcast socket.\n");
    }

    setsockopt (*broadcast_socket, SOL_SOCKET, SO_REUSEADDR, &trueval, sizeof (trueval));
    setsockopt (*broadcast_socket, SOL_SOCKET, SO_BROADCAST, &trueval, sizeof (trueval));

    if (bind (*broadcast_socket, (const struct sockaddr *)host_addr, sizeof (struct sockaddr_in)) < 0){
        printf("Couldn't bind to port %d \n", BROADCAST_PORT);
        return -1;
    }

    return 0;
}

#pragma pack(1)
typedef struct _data_packet{
    double i;
    double sin;
    double cos;
} data_packet_t;
#pragma pack()

void main(void){

    data_packet_t data_packet;
    int broadcast_sock = 0;
    struct sockaddr_in host_addr;
    struct timespec ts;

    ts.tv_sec = 0;
    ts.tv_nsec = RATE_MS;

    open_broadcast_socket(&broadcast_sock, &host_addr);

    for(double i = 0 ;; i += .1){

        data_packet.i = i;
        data_packet.sin = sin(i);
        data_packet.cos = cos(i);

        sendto(broadcast_sock,
               &data_packet,
               sizeof(data_packet),
               0,
               (struct sockaddr *) &host_addr,
               sizeof(host_addr));

        nanosleep(&ts, &ts);
    }
}

Compile and run the client on a machine different from the one you are on. Open wireshark, choose the interface attached to the 192.168.0 subnet.

WARNING

Running the client on the same machine as the one you are trying to receive the broadcast packets from will not work. The loopback interface will not receive the broadcast packets back from the network.

Wireshark Capture of UDP Packets

UDP Broadcasts Captured In Wireshark

Conclusion

You now have a program broadcasting on 192.168.0.255 port 9999. It is sending two floats sin and cos in a data packet.

Next we will begin writing an Electron App to parse and graph the stream of data.

← Back to all posts