Skip to Content
Course content

215: Building a Simple UDP Client and Server

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When people first move from TCP to UDP, they often walk in with a fundamental misunderstanding: they think of a UDP "connection" as a lightweight version of a TCP stream. I've seen many developers try to call connect() and then use send() and recv(), expecting the OS to maintain some kind of persistent session between the client and the server. They assume that because they "connected" to an IP, the socket now "belongs" to that specific peer.

UDP is not a connection, it's a postcard

Here is the reality: UDP is entirely connectionless. Using connect() on a UDP socket doesn't actually perform a handshake with the remote host; it just tells the local kernel, "Hey, if I use send(), just default to this address." It's a local convenience, not a network state.

Think of TCP like a phone call: you dial, the other person picks up, you both agree you're talking, and then you exchange data. UDP is a postcard. You write the address on the back, drop it in the mail, and hope it gets there. You don't know if the recipient is even home, and they don't know who you are until they read the "From" address on the card they just received.

Handling the destination with sockaddr_in

Since there is no persistent session, every single packet you send needs a destination, and every packet you receive comes with the sender's identity attached. This is why we use sendto() and recvfrom() instead of the basic send() and recv().

In a UDP server, you don't accept() a connection. You simply bind() your socket to a port and start listening. When a packet arrives, recvfrom() fills a sockaddr_in structure for you, telling you exactly who sent the data. This is how a single UDP server can handle requests from a thousand different clients using one single socket.

Implementing a Simple Heartbeat System

Let's look at a concrete example. Imagine we're building a basic telemetry system where a remote sensor (the client) sends a "heartbeat" status code to a monitoring station (the server). If the server stops receiving these, it knows the sensor is offline.

Here is how the server looks. Notice that we never call listen() or accept().

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int sockfd;
    char buffer[BUFFER_SIZE];
    struct sockaddr_in server_addr, client_addr;
    socklen_t addr_len = sizeof(client_addr);

    sockfd = socket(AF_INET, SOCK_DGRAM, 0); // SOCK_DGRAM is the key for UDP

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);

    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("Bind failed");
        exit(EXIT_FAILURE);
    }

    printf("Monitoring station listening on port %d...\n", PORT);

    while (1) {
        int len = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, 
                           (struct sockaddr *)&client_addr, &addr_len);
        
        buffer[len] = '\0';
        printf("Heartbeat received from %s: %s\n", 
               inet_ntoa(client_addr.sin_addr), buffer);
    }

    close(sockfd);
    return 0;
}

And here is the client. It doesn't need to "establish" anything; it just blasts the data toward the server's address.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 8080
#define SERVER_IP "127.0.0.1"

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char *message = "STATUS_OK";

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);

    // No connect() call here. We just send.
    sendto(sockfd, message, strlen(message), 0, 
           (struct sockaddr *)&server_addr, sizeof(server_addr));

    printf("Heartbeat sent to server.\n");

    close(sockfd);
    return 0;
}

I should mention that because UDP doesn't guarantee delivery, that "STATUS_OK" message could vanish into the void. In a real production system, you'd implement your own acknowledgement or timeout logic. But for simple telemetry or gaming state updates, the speed gain from skipping the TCP handshake is usually worth the risk.




📋 Practical Task

Build a UDP Echo-Response System

Modify the client and server provided in the lesson to create a "Request-Response" loop. Instead of a one-way heartbeat, implement the following:

  • The Client: Should send a specific string (e.g., "PING") to the server and then call recvfrom() to wait for a response.
  • The Server: Should receive the message, and instead of just printing it, use sendto() to send the string "PONG" back to the client_addr that was captured during the recvfrom() call.
  • Verification: The client should print "Server responded: PONG" before exiting.

Ensure your server can handle multiple "PINGs" from the client in a loop without crashing or requiring a restart.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.