Q:

clients connecting to TCP iterative server even after backlog queue is full

Here's an iterative server I've created to handle basic client-server chat application.
I am trying to run TCPserver on a terminal window and TCPclient on multiple terminal windows.
More than 5 the clients are getting connected(neither getting blocked nor failing. They immediately connect successfully) despite of the fact that I set the backlog value (listen system call) in server socket to be 5.
I expected that no more than 5 clients could be connected(only 1 would be accepted at a time).
Is my understanding of the backlog value set in the listen system call wrong?
Please clarify.

int listen(int sockfd, int backlog);
The backlog argument defines the maximum length  to  which  the queue of pending connections for sockfd may grow.

Here are the actual programs for reference.
TCPserver.c
#include<stdio.h>
#include<stdlib.h>

#include<sys/types.h>
#include<sys/socket.h>

#include<netinet/in.h>
#include<unistd.h>
#define BACKLOG 5

#include <netinet/in.h>
#include <arpa/inet.h>

#include<string.h>

int main()
{
    //create the server socket
    int sd;
    sd=socket(AF_INET,SOCK_STREAM,0);
    if(sd==-1)
    {
        perror("Some error occured in creating the socket: ");   
        //Interprets the value of errno as an error message, and prints it to stderr 
        exit(EXIT_FAILURE);
    }
    else
        printf("Socket created!\n");

    //define the server address
    struct sockaddr_in server_address;
    
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(9002);
    server_address.sin_addr.s_addr = INADDR_ANY; 
    //inet_addr("192.168.137.163");//INADDR_ANY;

    //bind the socket to our specified IP and port
    int bind_status = bind(sd, (struct sockaddr *) &server_address, sizeof(server_address));
    if(bind_status == -1)
    {
        perror("An error occurred in binding the socket: " );
        exit(EXIT_FAILURE);
    }
    else
        printf("Bind Successful!\n");

    //listen for connections
    int listen_status = listen(sd, BACKLOG);    
    if(listen_status == -1)
    {
        perror("Error occured in listening: ");
        exit(EXIT_FAILURE);
    } 
    else
        printf("Server is listening!\n");

    while(1)
    {
        //Accept a connection and create a new socket for this connection
        int new_sd;
        struct sockaddr_in client_address;
        int client_address_size = sizeof(client_address);

        new_sd= accept(sd, (struct sockaddr*) &client_address, &client_address_size);
        if(new_sd==-1)
        {
            perror("Can't accept connection: ");
            exit(EXIT_FAILURE);
        }
        else
            printf("Accept successful!\nA new client has connected. He'll soon send you a message.\n (You can chat or say \"exit\" to stop chatting)\n\n");
    

        //send a message to the client
        char buffer[256] = "Welcome to the server, lets chat! \n (You can chat or say \"exit\" to stop chatting)\n";
        send(new_sd,buffer,sizeof(buffer),0);

        //start chat
        while(1)
        {
            memset(buffer,0,256);
            int n = recv(new_sd, buffer, sizeof(buffer),0);
            if(n==-1 || strcmp(buffer,"exit\n")==0 || strcmp(buffer,"exit")==0)
                break;
            printf("\nclient said: %s\n",buffer);

            memset(buffer,0,256);
            printf("Say something: ");
            fgets(buffer,256,stdin);
            n = send(new_sd, buffer, sizeof(buffer), 0);
            if(n==-1 || strcmp(buffer,"exit\n")==0)
                break;
        }

        //close the sockets
        close(new_sd);
        printf("\nConnection ended. waiting for new connection now . . .\n");
    }

    close(sd);

    return 0;
}

and Here's the client
TCPclient.c
#include<stdio.h>
#include<stdlib.h>

#include<sys/types.h>
#include<sys/socket.h>

#include<netinet/in.h>

#include<unistd.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include<string.h>
int main()
{
    //create the socket
    int sd = socket(AF_INET,SOCK_STREAM,0);

    if(sd==-1)
    {
        perror("Some error occurred in creating the socket: ");
        exit(EXIT_FAILURE);
    }
    else
        printf("Socket created!\n");

    //specify an address for the socket
    struct sockaddr_in server_address;
    
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(9002);
    server_address.sin_addr.s_addr = inet_addr("127.0.0.1");//INADDR_ANY;

    //Connect to the server
    int connection_status = connect(sd,(struct sockaddr *) &server_address, sizeof(server_address));

    if(connection_status == -1)
    {
        perror("There was an error connecting to the remote socket: ");
        exit(EXIT_FAILURE);
    }
    else
        printf("Connected to the server! Waiting in the queue for the server to accept the connection...\n");
        
    char buffer[256];
    //get the connection message from the server
    recv(sd, &buffer, sizeof(buffer), 0);
    printf("Server Said: %s\n", buffer);

    //start chat
    while(1)
    {
        memset(buffer,0,256);
        printf("Say something: ");
        fgets(buffer,256,stdin);
        int n = send(sd,buffer,sizeof(buffer),0);
        if(n==-1 || strcmp(buffer,"exit\n")==0)
            break;       

        memset(buffer,0,256);
        n = recv(sd,buffer,sizeof(buffer),0);
        if(n==-1 || strcmp(buffer,"exit\n")==0)
            break;

        printf("\nServer said: %s\n",buffer);
    }

    //close the socket
    close(sd);

    return 0;
}

Someone suggested I check if syncookies have been enabled on my system. When I perform cat /proc/sys/net/ipv4/tcp_syncookies, I get 1.

A:

Your system has enabled SYN cookies, which allows the TCP stack to behave as if it has a very large listen queue. It was designed to mitigate DOS via SYN flooding.
The listen man page states about the backlog argument:

When syncookies are enabled
         there is no logical maximum length and this setting is ignored.

If you really want no more than 5 pending clients to wait on the server, you will have to manually maintain your own queue, and close new connections if your queue is full.
Note that this solution does not actually affect the behavior of the operating systems listen queue. The solution is to continually clear the listen queue of any backlog and close those connections if your server already has 5 pending connections in its own queue.
Probably in your case, the easiest way to achieve that is with two threads. One doing the accepting, the other handling connections off the queue.
The fragment below illustrates this in pseudo-code. It assumes proper thread mutual exclusion and signaling is performed by the queue operations.
accepting_thread () {
    int queue_count = 0;

    for (;;) {
        new_conn = accept();
        if (q_size(q) < 5) {
            q_enqueue(q, new_conn);
        } else {
            close(new_conn);
        }
    }
}

handling_thread () {
    for (;;) {
        new_conn = q_dequeue(q);
        /* ... */
        close(new_conn);
     }
}

To get clients that are terminated early to see a reset, you can enable the linger option with a 0 timeout value. Most TCP stacks will cause a RST to be generated when the socket is closed.

