C Programming

How to Listen for Connections on a Socket in C

Listening for connections on a socket is a very important aspect of developing the server applications in C language. By enabling a server to accept the incoming connections, it can establish a communication with the clients and provide the requested services. In this article, we will explore two approaches to achieve this: the “accept ()” method and the more advanced “epoll ()” method. These techniques have many different advantages which allow us to choose the most suitable approach based on our application requirements.

Using the Accept() Function to Listen for Connections on a Socket

The function which is known as “accept()” function is used to listen for connection on a socket. This function mainly blocks the program execution until and unless a client connection is created. As soon as a connection is made, this function returns a new socket descriptor that is used for client communication. Let us see the programming example which uses the “accept()” function for connection on a socket.

Programming Example 1:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main ()
{
    int server_soc, client_soc;
    struct sockaddr_in server_address, client_address;
    socklen_t client_length;

    // Create the server socket
    server_soc = socket (AF_INET, SOCK_STREAM, 0);
    // Bind the socket to a specific address and port
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = INADDR_ANY;
    server_address.sin_port = htons (7070);
    bind (server_soc, (struct sockaddr*)&server_address, sizeof(server_address));

    // Start listening for connections
    listen (server_soc, 5);
    printf ("Server is waiting for client connections \n");

    // Accept incoming connections
    client_length = sizeof (client_address);
    client_soc = accept (server_soc, (struct sockaddr*)&client_address, &client_length);
    printf ("Client has been connected! \n");

    // close the client socket
    close (client_soc);
    // Continue listening for more connections
    while (1) {
        client_soc = accept (server_soc, (struct sockaddr*)&client_address, &client_length);
        printf("Client has been connected! \n");
        close (client_soc);
    }

    // Close the server socket
    close (server_soc);
    return 0;
}

Output:

$ gcc soc.c -o soc
$ ./soc
Server is waiting for client connections

The programming example creates a server socket and binds it to a certain address and port. It then watches for new connections. When a client connects, a message which indicates the connection is shown. After that, this code continues to listen for new connections and prints the connection each time.

Using the Epoll() Function to Listen for Connections on a Socket

This function can also be used to listen for connections on a socket in the C programming language. Let us see the use of the “epoll()” function in our programming example.

Programming Example 2:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAX_EVENTS 10

int main () {
    int serverSocket, clientSocket;
    struct sockaddr_in serverAddress, clientAddress;
    socklen_t clientLength;
    // Create the server socket
    serverSocket = socket(AF_INET, SOCK_STREAM, 0);

    // Bind the socket to a specific address and port
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = INADDR_ANY;
    serverAddress.sin_port = htons(8080);
    bind (serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
    // Start listening for connections
    listen (serverSocket, 5);

    printf ("Server is waiting for client connections!!!\n");

    // Create epoll instance
    int epollfd = epoll_create1 (0);
    if (epollfd == -1) {
        perror ("epoll_create1");
        exit (EXIT_FAILURE);
    }
    // we have to register the server socket for “epoll” events
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = serverSocket;
    if (epoll_ctl (epollfd, EPOLL_CTL_ADD, serverSocket, &event) == -1) {
        perror ("epoll_ctl");
        exit (EXIT_FAILURE);
    }

    // Buffer to hold events
    struct epoll_event events[MAX_EVENTS];

    while (1) {
        int numEvents = epoll_wait (epollfd, events, MAX_EVENTS, -1);
        if (numEvents == -1) {
            perror ("epoll_wait");
            exit (EXIT_FAILURE);
        }
        // Process events
        for (int i = 0; i < numEvents; i++) {
            // Check if the event is for the server socket
            if (events[i].data.fd == serverSocket) {
                // Accept incoming connection
                clientLength = sizeof (clientAddress);
                clientSocket = accept (serverSocket, (struct sockaddr*)&clientAddress, &clientLength);
                printf ("Client connected!\n");
                // Further handling of the client connection will be done here
                // now we Close the client socket
                close (clientSocket);
            }
        }
    }
    // now we Close the server socket
    close (serverSocket);
    return 0;
}

Output:

$ gcc soc.c -o soc
$ ./soc
Server is waiting for client connections!!!

Here, we use the “epoll()” function to create a server. This server is listening for incoming connections. We can use the “epoll()” function when there are many clients connection. The server socket has “epoll()” and reads the registered events. A new connection is identified when it is discovered. In this program, we also use the “epoll_wait()” loop which waits for events and handles them in this program. Until and unless the program is expressly cancelled, the server continues to wait for connections from the client server.

Conclusion

Listening for connections on a socket in C is a fundamental aspect of server applications. We discussed about two approaches which are the traditional “accept()” method and the “epoll()”
method mechanism in the article. The “accept()” approach involves blocking and waiting for connections, while “epoll()” uses the event-driven programming to listen to multiple connections. Both methods have their strengths and we can use as per our programming requirements.

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