Open In App

Creating Unix Sockets

Last Updated : 05 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Sockets are a means to allow communication between two different processes over the same or different machines/networks. To be more precise, it’s a way to talk to other computers and exchange data. In Unix, every I/O action is done by writing or reading a file descriptor. Sockets are the end point of communication with each process having its socket and a socket descriptor for all the operations. In this article, we are going to read more about creating a socket in a Unix system and implementing both client and server-side programs.

Creating Server Script

The system calls for establishing a connection is different for both the client and the server, but both involve the basic construct of a socket. Both processes establish their sockets. In this section, we will create the server script in C language. So follow the steps and break down the script to understand the implementation of the server.

Step 1: Create a server.c File

Firstly, create the server.c file in the vim editor by using the command in the terminal.

vi server.c
Creating server.c file

Creating server.c file

Step 2: Server Initialization and Socket Binding

C
int server_fd, new_socket;
ssize_t valread;
struct sockaddr_in address;
int opt = 1;
socklen_t addrlen = sizeof(address);
char buffer[1024] = { 0 };

// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket failed");
    exit(EXIT_FAILURE);
}

// Forcefully attaching socket to the port 8080
if (setsockopt(server_fd, SOL_SOCKET,
               SO_REUSEADDR | SO_REUSEPORT, &opt,
               sizeof(opt))) {
    perror("setsockopt");
    exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

// Forcefully attaching socket to the port 8080
if (bind(server_fd, (struct sockaddr*)&address,
         sizeof(address))
    < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
  • Socket Creation: The server uses the socket system call to create a socket that accepts a request coming from the client.
  • Server Address Configuration: The server’s address structure (address) is configured with the server’s IP, port, and protocol details. We will use the socket family of AF_INET, we will also use the standard socket type of SOCK_STREAM for TCP connection. The function after execution will return a file descriptor to the new socket that can be used to refer to the socket in future system calls.
  • Binding: The socket is bound to the server’s address i.e. a local address using the bind system call.
  • Printing Server Listening: A message is printed indicating that the server is listening on a specific port.

Step 3: Listening Multiple Clients

C
int server_fd, new_socket;
ssize_t valread;
struct sockaddr_in address;
int opt = 1;
socklen_t addrlen = sizeof(address);
char buffer[1024] = { 0 };

// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket failed");
    exit(EXIT_FAILURE);
}

// Forcefully attaching socket to the port 8080
if (setsockopt(server_fd, SOL_SOCKET,
               SO_REUSEADDR | SO_REUSEPORT, &opt,
               sizeof(opt))) {
    perror("setsockopt");
    exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

// Forcefully attaching socket to the port 8080
if (bind(server_fd, (struct sockaddr*)&address,
         sizeof(address))
    < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);

// Listening multiple clients
if (listen(server_fd, 3) < 0) {
    perror("listen");
    exit(EXIT_FAILURE);
}
  • Client Connection Handling: The server calls the listen() function to listen for incoming connections. In the function, we pass the socket we want to listen to. We also pass the maximum number of connections to the queue in the socket as the 2nd argument. In the code, we specified 5 max connection queues.

Step 4: Handling Individual Clients using the accept function

C
if ((new_socket = accept(
         server_fd, (struct sockaddr*)&address, &addrlen))
    < 0) {
    perror("accept");
    exit(EXIT_FAILURE);
}
valread = read(new_socket, buffer,
               1024 - 1); // subtract 1 for the null
                          // terminator at the end
printf("%s\n", buffer);
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent\n");
  • Accepting client connection: To accept an incoming connection from the client, we can use the accept() function. The function will take the server socket as an argument and retrieve the first socket connection in the queue, create a new socket through which we can communicate with the client, not to use the file descriptor given by accept() and not from the socket() call and return the file descriptor for that socket.
  • Reading data: we can read() and write() system calls to start communication with the peer socket (i.e. to communicate with the client). We read the message sent by the client and store it in a character buffer.
  • Printing the message: Print the message stored in the buffer array.
  • Server Message Sending: send back a message to the client for duplex communication.

Step 5: Transferring and Data Cleanup

C
// closing the connected socket
close(new_socket);
// closing the listening socket
close(server_fd);
return 0;
  • Socket Closure: The server socket is closed using close.
  • Program Termination: The program returns 0 to indicate successful termination.

Step 6: Write the Complete Code

Now, write the complete code on the server.c file.

C
// Server side C program to demonstrate Unix Socket

#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080
int main(int argc, char const* argv[])
{
    int server_fd, new_socket;
    ssize_t valread;
    struct sockaddr_in address;
    int opt = 1;
    socklen_t addrlen = sizeof(address);
    char buffer[1024] = { 0 };
    char* hello = "Hello from server";

    // Creating socket file descriptor
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // Forcefully attaching socket to the port 8080
    if (setsockopt(server_fd, SOL_SOCKET,
                   SO_REUSEADDR | SO_REUSEPORT, &opt,
                   sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // Forcefully attaching socket to the port 8080
    if (bind(server_fd, (struct sockaddr*)&address,
             sizeof(address))
        < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    printf("Server listening on port %d...\n", PORT);
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    if ((new_socket
         = accept(server_fd, (struct sockaddr*)&address,
                  &addrlen))
        < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }
    valread = read(new_socket, buffer,
                   1024 - 1); // subtract 1 for the null
                              // terminator at the end
    printf("%s\n", buffer);
    send(new_socket, hello, strlen(hello), 0);
    printf("Hello message sent\n");

    // closing the connected socket
    close(new_socket);
    // closing the listening socket
    close(server_fd);
    return 0;
}
Server Script Code

Server Script Code

Creating Client Script

In this section, we will create the Client script through which multiple clients can connect to the server for data transfer and message transfer.

Step 1: Create a client.c File

Firstly, create the client.c file in the nano editor by using the command in the terminal.

vi client.c
Creating client.c file

Creating client.c file

Step 2: Client Initialization

C
int status, valread, client_fd;
struct sockaddr_in serv_addr;
char* hello = "Hello from client";
char buffer[1024] = { 0 };
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    printf("\n Socket creation error \n");
    return -1;
}

serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);

// Convert IPv4 and IPv6 addresses from text to binary
// form
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)
    <= 0) {
    printf("\nInvalid address/ Address not supported \n");
    return -1;
}
  • Client Structure Definition: A structure (Client) is defined to hold information about the server address and the client socket.
  • Client Initialization: An instance of the Client structure (client) is created to store client-related information.

Step 3: Message Sending and Cleanup

C
if ((status
     = connect(client_fd, (struct sockaddr*)&serv_addr,
               sizeof(serv_addr)))
    < 0) {
    printf("\nConnection Failed \n");
    return -1;
}
send(client_fd, hello, strlen(hello), 0);
printf("Hello message sent\n");
valread = read(client_fd, buffer,
               1024 - 1); // subtract 1 for the null
                          // terminator at the end
printf("%s\n", buffer);

// closing the connected socket
close(client_fd);
return 0;
  • Message Sending: The client connects to the server, and sends the message to the server using send.
  • Message Printing: The received message is printed to the console from the server.
  • Socket Closure: When the program terminates, the client socket is closed using close.

Step 4: Write the Complete Code

Now, write the complete code for the client.c file.

C
// Client side socket
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080

int main(int argc, char const* argv[])
{
    int status, valread, client_fd;
    struct sockaddr_in serv_addr;
    char* hello = "Hello from client";
    char buffer[1024] = { 0 };
    if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // Convert IPv4 and IPv6 addresses from text to binary
    // form
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)
        <= 0) {
        printf(
            "\nInvalid address/ Address not supported \n");
        return -1;
    }

    if ((status
         = connect(client_fd, (struct sockaddr*)&serv_addr,
                   sizeof(serv_addr)))
        < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }
    send(client_fd, hello, strlen(hello), 0);
    printf("Hello message sent\n");
    valread = read(client_fd, buffer,
                   1024 - 1); // subtract 1 for the null
                              // terminator at the end
    printf("%s\n", buffer);

    // closing the connected socket
    close(client_fd);
    return 0;
}
Client Script Code

Client Script Code

Steps to Execute Scripts

Step 1: Compile Server and Client Scripts

Once we have created the scripts, we need to compile them with the gcc compiler. So execute the below commands in the terminal to compile it successfully:

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

Step 2: Checking Socket Communication

In the below output, we can see that we have executed server and multiple client scripts.

  • server.c
Server Listening for Communication

Server Listening for Communication


  • client.c
Client Sends Message

Client Sends Message

Conclusion

In conclusion, Sockets are one of the best ways to make strong and effective communication. IPC (inter-process communication) inside the same host has made strong Unix socket programming in C. It helps to build reliable and high-performance communication channels for processes operating on the same computer by using Unix domain sockets. The client initiates a TCP socket, configures the server address, and connects for message reception.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads