C Programming

How to Implement a Simple Client-Server Socket Program in C

The client-server socket programming is very essential to enable the communication between various processes on a network in networked applications. We may need to create connections, exchange data, and create interactive applications using sockets.

Let us discuss the fundamentals by implementing a simple client-server socket program in the C language. Let us also discuss about the essential steps such as socket creation, binding, listening, and accepting connections. We will learn more about socket programming with the C programming language by understanding this article.

Definition of a Socket

A socket is a key concept in computer networking that enables communication between processes that operate on various machines across a network. It functions as an endpoint for data transmission and reception between the client and server.

Let us consider a socket as a virtual communication channel that allows us with a connection and information exchange between two processes. Data packets can enter and exit through it, serving as a door to ensure a reliable and efficient communication.

The TCP (Transmission Control Protocol) sockets, referred to as stream sockets, offer a dependable and well-organized stream of data transmission. They ensure that the supplied information from one end reaches the other end undamaged and in the proper manner.

The UDP (User Datagram Protocol) sockets, also known as datagram sockets, allow the exchange of single packets, or datagrams, without the guarantee of stability. Datagram sockets are often used when real-time communication or low-latency transmission is required.

The sockets are identified by an IP address and a port number’s combination. The IP address describes the machine’s location in the network, while the port number identifies the specific application or process that runs on that machine. This combination enables the sockets to establish a connection and ensure that the data reaches our target.

Let us see the following diagram that explains the client-server architecture:

Programming Example 1: Server-Side Program

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <unistd.h>

#define PORT 7070

#define BUFFER_SIZE 1024

int main()

{

    int serverSocket, newSocket, valRead;
    struct sockaddr_in serverAddress, clientAddress;
    int opt = 1;
    int addressLength = sizeof(serverAddress);
    char buffer[BUFFER_SIZE] = {0};
    char message[BUFFER_SIZE] = {0};

    // Create server socket
    if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) == 0)
    {
        perror ("Socket creation failed");
        exit (EXIT_FAILURE);
    }
    printf ("Socket successfully created.\n");

    // Set socket options
    if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
    {
        perror("setsockopt failed");
        exit(EXIT_FAILURE);
    }

    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = INADDR_ANY;
    serverAddress.sin_port = htons(PORT);

    // Bind


    if (bind(serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0)

    {
        perror("Bind failed");
        exit(EXIT_FAILURE);
    }
    printf("Socket successfully binded.\n");

    // Listen for connections


    if (listen(serverSocket, 3) < 0)

    {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }
    printf("Server listening...\n");

    // Accept incoming connection


    if ((newSocket = accept(serverSocket, (struct sockaddr *)&clientAddress, (socklen_t *)&addressLength)) < 0)

    {
        perror("Accept failed");
        exit(EXIT_FAILURE);
    }
    printf("Server accepted the client...\n");

    // Start chat
    while (1)
    {
        // Read message from client
        valRead = read(newSocket, buffer, BUFFER_SIZE);
        printf("Client: %s\n", buffer);

        // Check for client exit command
        if (strcmp(buffer, "exit") == 0)
            break;

        // Clear the buffer and message
        memset (buffer, 0, BUFFER_SIZE);
        memset (message, 0, BUFFER_SIZE);

        // Get server message
        printf("Server: ");
        fgets(message, BUFFER_SIZE, stdin);

        // Send server message to client
        send(newSocket, message, strlen(message), 0);

        // Check for server exit command
        if (strcmp(message, "exit\n") == 0)
            break;
    }

    // Close sockets
    close (newSocket);
    close (serverSocket);

    return 0;


}

Programming Example 2: Client-Side Program

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <unistd.h>

#define PORT 7070

#define BUFFER_SIZE 1024

int main()

{

    int clientSocket, valRead;
    struct sockaddr_in serverAddress;
    char buffer[BUFFER_SIZE] = {0};
    char message[BUFFER_SIZE] = {0};

    // Create client socket
    if ((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) == 0)
    {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }
    printf("Socket successfully created.\n");

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


    if (inet_pton(AF_INET, "127.0.0.1", &(serverAddress.sin_addr)) <= 0)

    {
        perror("Invalid address/ Address not supported");
        exit(EXIT_FAILURE);
    }
    // Connect to the server

    if (connect(clientSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0)

    {
        perror("Connection failed");
        exit(EXIT_FAILURE);
    }
    printf("Connected to the server.\n");

    // Start chat
    while (1)
    {
        // Clear the buffer and message
        memset(buffer, 0, BUFFER_SIZE);
        memset(message, 0, BUFFER_SIZE);

        // Get client message
        printf("Client: ");
        fgets (message, BUFFER_SIZE, stdin);

        // Send client message to the server
        send(clientSocket, message, strlen(message), 0);

        // Check for client exit command
        if (strcmp(message, "exit\n") == 0)
            break;

        // Read message from server
        valRead = read (clientSocket, buffer, BUFFER_SIZE);
        printf ("Server: %s\n", buffer);

        // Check for server exit command
        if (strcmp(buffer, "exit") == 0)
            break;
    }

    // Close socket
    close (clientSocket);

    return 0;


}

Output:

**Server program compilation**

$ gcc soc.c -o soc

$ ./soc

Client: good morning, sir!

Server: follow your task in your email

Client: okay, sure!

**Client program compilation**

$ gcc cli.c -o cli

$ ./cli

Socket successfully created.

Connected to the server.

Client: good morning, sir!

Server: follow your task in your email

Client: okay, sure!

Explanation:

In these programming examples, the codes implement a simple chat application on the port 7070 where the client connects to the server, writes messages, and receives replies up until an “exit” command is received. The server waits for and accepts the client connections, and reads and reacts to the client messages.

Conclusion

This article shows how a simple client-server socket program is implemented in the C language. These programming examples provide a starting point to create more complex networked applications using the sockets in the C programming language by establishing connections, transferring messages, and managing a client-server communication.

About the author

Bamdeb Ghosh

Bamdeb Ghosh is having hands-on experience in Wireless networking domain.He's an expert in Wireshark capture analysis on Wireless or Wired Networking along with knowledge of Android, Bluetooth, Linux commands and python. Follow his site: wifisharks.com