Tuesday, November 10, 2015

UNIX socket communication with threads

Here's a quick and dirty example of how to have two threads communicating over a UNIX socket on Linux. These could have been two different processes, but I used threads so the example was more self contained. Note that this uses the gethrtime() function I described on another post.
/*
 * gcc -pthread -lrt -O3 socket.c -o socket
 */
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <err.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>

#define SOCKET_FILE    "/tmp/socket_file"

typedef struct {
    char    reply[256];
} socket_msg_t;

static volatile server_socket = 0;

void *
server_thread(void *arg)
{
    socket_msg_t          msg;
    struct sockaddr_un    addr;
    int                   sock;
    int                   conn_socket;
    int                   max;
    int                   nfds;
    fd_set                fds;

    if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        fprintf(stderr, "server failed to open socket\n");
        return (NULL);
    }

    bzero(&addr, sizeof (addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SOCKET_FILE, sizeof (addr.sun_path));
    unlink(SOCKET_FILE);

    if (bind(sock, (struct sockaddr *)&addr, sizeof (addr)) != 0) {
        fprintf(stderr, "server can't bind\n");
        close(sock);
        server_socket = -1;
        return (NULL);
    }

    if (listen(sock, 2) != 0) {
        fprintf(stderr, "server can't listen\n");
        close(sock);
        server_socket = -1;
        return (NULL);
    }

    server_socket = sock;

    for (;;) {
        FD_ZERO(&fds);
        FD_SET(server_socket, &fds);
        max = server_socket;

        if ((nfds = select(max + 1, &fds, NULL, NULL, NULL)) == -1) {
            if (errno == EINTR) {
                continue;
            }
            close(server_socket);
            server_socket = -1;
            return (NULL);
        }

        if (!FD_ISSET(server_socket, &fds)) {
            continue;
        }

        if ((conn_socket = accept(server_socket, NULL, NULL)) == -1) {
            close(server_socket);
            server_socket = -1;
            return (NULL);
        }

        while (conn_socket != -1) {
            if (read(conn_socket, (caddr_t)&msg, sizeof (msg)) !=
                sizeof (msg)) {
                fprintf(stderr, "server received bad msg\n");
                conn_socket = -1;
            }
   
            snprintf(msg.reply, sizeof (msg.reply),
                "replying at %ld\n", gethrtime());
   
                    if (write(conn_socket, (caddr_t)&msg,
                sizeof (msg)) < 0) {
                fprintf(stderr, "server failed to reply\n");
                conn_socket = -1;
                    }

            fprintf(stderr, "server replied\n");
        }

        close(conn_socket);
    }
}

void *
client_thread(void *arg)
{
    socket_msg_t          msg;
    struct sockaddr_un    addr;
    int                   cli_socket;

    while (server_socket == 0) {
        if (server_socket == -1) {
            fprintf(stderr, "client exitting..\n");
            break;
        }

        sleep(1);
    }

    sleep(1);

    if ((cli_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        fprintf(stderr, "client failed to open socket\n");
        return (NULL);
    }

    bzero(&addr, sizeof (addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SOCKET_FILE, sizeof (addr.sun_path));

    if (connect(cli_socket, (struct sockaddr*)&addr,
sizeof (addr)) == -1) {
        fprintf(stderr, "client failed to connect\n");
        return (NULL);
    }

    fprintf(stderr, "client connected\n");

    while (server_socket != -1) {
        if (write(cli_socket, &msg, sizeof (msg)) < 0) {
            perror("");
            fprintf(stderr, "client failed to write\n");
            break;
        }

        if (read(cli_socket, &msg, sizeof (msg)) != sizeof (msg)) {
            fprintf(stderr, "client failed to read\n");
            break;
        }

        fprintf(stderr, "client received :: %s\n", msg.reply);
        sleep (1);
    }

    fprintf(stderr, "client server_socket closed\n");
    close(cli_socket);
}

int
main(int argc, char **argv)
{
    pthread_t server, client;

    if (pthread_create(&server, NULL, server_thread, NULL) != 0) {
        perror("failed to create waiter thread");
        return (1);
    }

    if (pthread_create(&client, NULL, client_thread, NULL) != 0) {
        perror("failed to create signaler thread");
        return (1);
    }

    pthread_join(server, NULL);
    pthread_join(client, NULL);

    return (0);
}
And some sample output:
client connected
server replied
client received :: replying at 16116710538870904

server replied
client received :: replying at 16116711539198484

server replied
client received :: replying at 16116712539583206

No comments:

Post a Comment