Table of Contents

    Client server in C using socket in Linux

    1. Continuous communication with UNIX Sockets

    Let's look at an example with a client and a server to show how data can be sent back and forth non-stop between two programs using UNIX sockets. In this example, the server waits for requests from the client, processes these requests (like doing something simple such as flipping a string of characters around or giving back the current date and time), and then sends the result back to the client. The client, after getting each answer, sends a new request. This back-and-forth keeps going until the client sends a signal to stop, like a specific message ("QUIT"), after which the server closes the connection.

     

    To clarify, it is essential that the steps outlined are followed to facilitate communication between a client and a server. Following this paragraph, pseudo code will be provided to demonstrate the process. This pseudo-code is necessary for anyone wishing to implement this in another programming language.

    • Server
      - Make a UNIX socket.
      - Connect the socket to an address (a socket file).
      - Listen on the socket for coming connections.
      - Accept a client connection.
      - Keep doing this until the message "QUIT" is received:
             ->Read the client's request.
             ->Process the request (for example, flip the content).
             ->Send the response to the client.
      - Close the client socket.
      - Clean up (close the server socket, delete the socket file).
    • Client
      - Make a UNIX socket.
      - Connect to the server socket.
      - Keep doing this a set number of times or until a specific condition is met:
              ->Send a request to the server (for example, a string of characters).
              ->Wait for and read the server's response.
              ->Process/display the response.
      - Send the "QUIT" message to the server to indicate the end.
      - Close the socket.

    2. Example of Pseudo-Code


    Server:

    /* Server pseudo-code */
    socket = create_socket()
    bind_socket(socket, "/tmp/socket")
    listen(socket)
    client_socket = accept_connection(socket)
    while true:
       request = read(client_socket)
       if request == "QUIT":
           break
       response = process_request(request)
       write(client_socket, response)
    close(client_socket)
    cleanup(socket)  // Close socket and delete socket file
    

    Client:

    /* Client pseudo-code */
    socket = create_socket()
    connect(socket, "/tmp/socket")
    for each data in data:
       write(socket, data)
       response = read(socket)
       display(response)
    write(socket, "QUIT")
    close(socket)
    

    Request Processing (Example)
    Processing the request can be simple or complex depending on what the application needs. For example, if the server gets a string of characters, it could flip it around and send the result back:

    
    function process_request(request):
       return request.reverse()
    

    3. Loop Communication
    The main point of this example is the cycle of request-response: the client sends a request, the server responds, and this process repeats. This allows for ongoing, interactive communication between the client and the server. Ending the loop is controlled by an explicit stop signal (QUIT in this case), letting each side prepare and carry out clean-up operations before ending the connection.

     

    This example shows the basic flow of socket communication for multiple data exchanges. Real implementations might add more complexity like handling errors, multiplexing to deal with many connections simultaneously, or more sophisticated protocols for structuring the exchanged data.

     

    Here's a simple example to put into practice the pattern of continuous data exchange between a client and a server using UNIX sockets in C. This example demonstrates how a server can receive strings of characters from the client, process them (in this example, by echoing them back as received), and return them to the client. The client sends several messages, then ends the communication by sending QUIT.

    /**Server in C**/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    
    #define SOCKET_PATH "/tmp/example_socket"
    #define BUFFER_SIZE 100
    
    int main() {
        int server_fd, client_fd, len;
        struct sockaddr_un server_addr, client_addr;
        char buffer[BUFFER_SIZE];
    
        if ((server_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            exit(EXIT_FAILURE);
        }
    
        memset(&server_addr, 0, sizeof(struct sockaddr_un));
        server_addr.sun_family = AF_UNIX;
        strcpy(server_addr.sun_path, SOCKET_PATH);
    
        unlink(SOCKET_PATH);
    
        if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_un)) == -1) {
            perror("bind");
            exit(EXIT_FAILURE);
        }
    
        if (listen(server_fd, 5) == -1) {
            perror("listen");
            exit(EXIT_FAILURE);
        }
    
        printf("Server waiting for connections...\n");
    
        if ((client_fd = accept(server_fd, NULL, NULL)) == -1) {
            perror("accept");
            exit(EXIT_FAILURE);
        }
    
        while (1) {
            len = read(client_fd, buffer, BUFFER_SIZE - 1);
            if (len > 0) {
                buffer[len] = '\0';
                printf("Received: %s\n", buffer);
                if (strcmp(buffer, "QUIT") == 0) {
                    break;
                }
                write(client_fd, buffer, len);
            } else if (len == 0) {
                printf("Client disconnected\n");
                break;
            } else {
                perror("read");
                exit(EXIT_FAILURE);
            }
        }
    
        close(client_fd);
        close(server_fd);
        unlink(SOCKET_PATH);
    
        return 0;
    }
    
    /**Client in C**/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    
    #define SOCKET_PATH "/tmp/example_socket"
    #define BUFFER_SIZE 100
    
    int main() {
        int sock_fd;
        struct sockaddr_un server_addr;
        char buffer[BUFFER_SIZE];
    
        if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            exit(EXIT_FAILURE);
        }
    
        memset(&server_addr, 0, sizeof(struct sockaddr_un));
        server_addr.sun_family = AF_UNIX;
        strcpy(server_addr.sun_path, SOCKET_PATH);
    
        if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_un)) == -1) {
            perror("connect");
            exit(EXIT_FAILURE);
        }
    
        while (printf("> "), fgets(buffer, BUFFER_SIZE, stdin) != NULL) {
            buffer[strcspn(buffer, "\n")] = 0; // Remove newline
            write(sock_fd, buffer, strlen(buffer));
            if (strcmp(buffer, "QUIT") == 0) {
                break;
            }
            read(sock_fd, buffer, BUFFER_SIZE);
            printf("Echo: %s\n", buffer);
        }
    
        close(sock_fd);
    
        return 0;
    }

    4. Compilation and Execution
    Compile the server and client using gcc or another C compiler. For example:
     

    gcc server.c -o server
    gcc client.c -o client

     

    Start the server first, in one terminal:
    ./server

     

    Then, in another terminal, start the client:
    ./client

     

    In the client's terminal, type messages to send to the server. The server sends back each message (echoes it). To end the session, type QUIT.

    This simple example shows how to set up two-way communication between a client and a server on the same system using UNIX sockets. It allows for many data exchanges until the client decides to end the session.

     

    5. Test on on Raspberry Pi Uinsg VsCode

    I've executed both the client and server on my remote board, utilizing VSCode for the example. VSCode is my tool of choice for debugging and developing on my system via an SSH connection. It's possible to run commands directly in the terminal and compile each file as outlined below.

    📝 Article Author : SEMRADE Tarik
    🏷️ Author position : Embedded Software Engineer
    🔗 Author LinkedIn : LinkedIn profile

    Comments