UPD sockets communication

2019-02-01

Categories: notes Tags: C network unix

Simple UPD client-server app that demonstrates bidirectional communication using UPD sockets.

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define PORT_PARENT 8080
#define PORT_CHILD  8081
#define BUFSZ       1024

#define dbg(_PRNTMSG, ...) \
    printf("[pid=%ld] " _PRNTMSG "\n", (long)getpid(), ##__VA_ARGS__);

int main(int argc, char const *argv[])
{
    int rc;
    int fd;
    socklen_t len;
    ssize_t recv_nb;
    char rcv_buf[BUFSZ], snd_buf[BUFSZ];
    char msg[128] = "Hello";
    struct sockaddr_in parent_addr, child_addr;
    pid_t child_pid;

    /* Set up parent connection credentials */
    parent_addr.sin_family = AF_INET;
    parent_addr.sin_addr.s_addr = INADDR_ANY;
    parent_addr.sin_port = htons(PORT_PARENT);

    /* Set up child connection credentials */
    child_addr.sin_family = AF_INET;
    child_addr.sin_addr.s_addr = INADDR_ANY;
    child_addr.sin_port = htons(PORT_CHILD);

    switch (child_pid = fork()) {
    case -1: {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    case 0: {
        /* Initialize child socket */
        fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (fd < 0) {
            perror("socket");
            exit(EXIT_FAILURE);
        }

        /* Assignee address to child socket */
        rc =
            bind(fd, (const struct sockaddr *)&child_addr, sizeof(parent_addr));
        if (rc == -1) {
            perror("bind");
            exit(EXIT_FAILURE);
        }

        for (size_t i = 0;; i++) {
            snprintf(msg, BUFSZ, "child_data_%zu", i);
            dbg("Sending: %s", msg);
            sendto(fd, (const char *)msg, strlen(msg), MSG_CONFIRM,
                (const struct sockaddr *)&parent_addr, sizeof(parent_addr));

            recv_nb = recvfrom(fd, (char *)rcv_buf, BUFSZ, MSG_WAITALL,
                (struct sockaddr *)&parent_addr, &len);
            dbg("Received from parent: %s", rcv_buf);
        }

        dbg("terminate");
        fflush(stdout);
        exit(EXIT_SUCCESS);
    }
    default:
        break;
    }

    /* Initialize parent socket */
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    /* Assignee address to parent socket */
    rc = bind(fd, (const struct sockaddr *)&parent_addr, sizeof(parent_addr));
    if (rc == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    for (size_t i = 0; i < 5; i++) {
        recv_nb = recvfrom(fd, (char *)rcv_buf, BUFSZ, MSG_WAITALL,
            (struct sockaddr *)&child_addr, &len);
        if (recv_nb == -1) {
            perror("recvfrom");
            break;
        }
        dbg("Received from child: %s", rcv_buf);

        snprintf(msg, BUFSZ, "parent_data_%zu", i);
        dbg("Sending: %s", msg);
        rc = sendto(fd, (const char *)msg, strlen(msg), MSG_CONFIRM,
            (const struct sockaddr *)&child_addr, len);
        if (rc == -1) {
            perror("sendto");
            break;
        }
        dbg("Sent");

        usleep(1000);
    }

    /* Terminate child */
    dbg("Terminate child");
    kill(child_pid, SIGTERM);

    /* Waiting for child */
    int return_status;
    if (waitpid(child_pid, &return_status, 0) == -1) {
        perror("waitpid");
        exit(EXIT_FAILURE);
    }

    return 0;
}