Brain Dump

UDP

Tags
networking

Is a connectionless (stateless) protocol built on top of IPv4 and IPv6. It's simple: Decide the destination (address + port) -> Send your packet. The protocol makes no guarantees about whether the packet reached the destination or not.

UDP is fire and forget.

UDP is commonly used when receiving up to date data is more important than receiving all of the data. For example a game may send continuous updates of player positions or a video signal may send picture updates through UDP. Losing part of the message is acceptable because you can work with what you've already received or it may not be worth the bandwidth costs of re-transmitting a packet because it was dropped.

Socket Procedure

UDP sockets don't use the write/read syscalls like TCP sockets or file descriptors. Instead we use the sendto syscall to transmit a message and the recvfrom call to read packets back from the connected address.

This works much the same way as a TCP socket except there's no need to open a connection before sending or listening for packets.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>

#define port "5336"

int sockfd;

struct addrinfo hints, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;

int s = getaddrinfo(NULL, port, &hints, &res);
if (s != 0) {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
    exit(1);
}

sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

Note: that if you perform a partial read from a packet, the rest of that data is discarded. One call to recvfrom is one packet. To make sure that you have enough space, use 64 KiB as storage space.

// Bind to the socket to the local port so you can listen for connections.
if (bind(sockfd, res->ai_addr, res->ai_addrlen) != 0) {
    perror("bind()");
    exit(1);
}

// Generate a struct to store the address information of the recieved data.
struct sockaddr_storage addr;
int addrlen = sizeof(addr);

// Read upto 1000 bytes from the open socket.
char buf[1000];
ssize_t byte_count = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *__restrict)&addr, (socklen_t *__restrict)&addrlen);

if (byte_count < 0) {
    perror("recvfrom()");
    exit(1);
}

printf("Recieved %ld bytes\n", byte_count);
printf("Message was: %s\n", buf);

close(sockfd);
Code Snippet 1: An example of a UDP server.
#define SOCKET_TIMEOUT 1000

// Immediately clear the port on socket close.
int optval = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));

// Set a timeout for the request.
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = SOCKET_TIMEOUT;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

// The socket is now connected and ready to use.
// Note the lack of a need to connect to the destination address.

// Send a message to the dest and write out the number of read bytes.
const char *to_send = "Hello!\n";
int send_rett = sendto(sockfd, to_send, strlen(to_send), 0, addr->ai_addr, addr->ai_addrlen);
printf("Recieved back %d\n", send_rett);

close(sockfd);
Code Snippet 2: Example of setting up a client using UDP on localhost (127.0.0.1).