Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a server with listen(sockfd, 2) call able to accept 3 connections?

I am trying to understand how the backlog parameter in int listen(int sockfd, int backlog); affects how new connections are handled.

Here is my server program.

/* server.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>

int main()
{
    int sockfd;
    int ret;
    int yes = 1;

    struct addrinfo hints, *ai;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    if ((ret = getaddrinfo(NULL, "8000", &hints, &ai)) == -1) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
        return 1;
    }

    sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
    if (sockfd == -1) {
        perror("server: socket");
        return 1;
    }

    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) {
        perror("server: setsockopt");
        close(sockfd);
        return 1;
    }

    if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) == -1) {
        perror("server: bind");
        close(sockfd);
        return 1;
    }

    freeaddrinfo(ai);

    if (listen(sockfd, 2) == -1) {
        perror("server: listen");
        close(sockfd);
        return 1;
    }

    printf("server: listening ...\n");
    printf("server: sleep() to allow multiple clients to connect ...\n");
    sleep(10);

    printf("server: accepting ...\n");
    while (1) {
        int connfd;
        struct sockaddr_storage client_addr;
        socklen_t client_addrlen = sizeof client_addr;
        char buffer[1024];
        int bytes;

        connfd = accept(sockfd, (struct sockaddr *) &client_addr, &client_addrlen);
        if (connfd == -1) {
            perror("server: accept");
            continue;
        }

        if ((bytes = recv(connfd, buffer, sizeof buffer, 0)) == -1) {
            perror("server: recv");
            continue;
        }

        printf("server: recv: %.*s\n", (int) bytes, buffer);
        close(connfd);
    }

    return 0;
}

Here is my client program.

/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>

int main(int argc, char **argv)
{
    int sockfd;
    int ret;
    struct addrinfo hints, *ai;

    if (argc != 2) {
        fprintf(stderr, "usage: %s MSG\n", argv[0]);
        return 1;
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    if ((ret = getaddrinfo(NULL, "8000", &hints, &ai)) == -1) {
        fprintf(stderr, "client: getaddrinfo: %s\n", gai_strerror(ret));
        return 1;
    }

    sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
    if (sockfd == -1) {
        perror("client: socket");
        return 1;
    }

    if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) == -1) {
        perror("client: connect");
        close(sockfd);
        return -1;
    }

    printf("client: connected\n");

    if (send(sockfd, argv[1], strlen(argv[1]), 0) == -1) {
        perror("client: send");
        close(sockfd);
        return -1;
    }

    printf("client: send: %s\n", argv[1]);

    freeaddrinfo(ai);
    close(sockfd);

    return 0;
}

I compile and run these programs with the following script.

# run.sh
gcc -std=c99 -Wall -Wextra -Wpedantic -D_DEFAULT_SOURCE server.c -o server
gcc -std=c99 -Wall -Wextra -Wpedantic -D_DEFAULT_SOURCE client.c -o client 
./server &
sleep 1
./client hello1 &
sleep 1
./client hello2 &
sleep 1
./client hello3 &
sleep 1
./client hello4 &
sleep 1
./client hello5 &
sleep 5
pkill server

When I run the above script, I get this output.

$ sh run.sh 
server: listening ...
server: sleep() to allow multiple clients to connect ...
client: connected
client: send: hello1
client: connected
client: send: hello2
client: connected
client: send: hello3
client: connected
client: send: hello4
client: connected
client: send: hello5
server: accepting ...
server: recv: hello1
server: recv: hello2
server: recv: hello3

The output shows that while the server was sleeping between listen() and accept(), all five clients could successfully connect() and send() to the server. However, the server could accept() and recv() three clients only.

I don't understand the following.

  1. The server program invokes listen() with the backlog parameter as 2. Why did all five clients succeed in connect()-ing then? I was expecting only 2 connect()s to be successful.
  2. Why was the server able to accept() and recv() from 3 clients instead of 2?
like image 698
Lone Learner Avatar asked Jan 05 '17 15:01

Lone Learner


1 Answers

The server program invokes listen() with the backlog parameter as 2. Why did all five clients succeed in connect()-ing then?

backlog parameter is only a hint for listen(). From POSIX doc:

The backlog argument provides a hint to the implementation which the implementation shall use to limit the number of outstanding connections in the socket's listen queue. Implementations may impose a limit on backlog and silently reduce the specified value. Normally, a larger backlog argument value shall result in a larger or equal length of the listen queue. Implementations shall support values of backlog up to SOMAXCONN, defined in .

like image 161
ks1322 Avatar answered Sep 25 '22 01:09

ks1322