2
votes

I am learning socket programming in c and trying to understand what exactly is listen() doing in the code. I understand it defines the maximum number of connection that can be queued for accept() but other than that I can't seem to understand what else is it doing.

According to the text I'm learning from,

listen() waits for incoming connections. Accept() gets the pending connection on the port you are listen()ing.

Looking at the flow of execution, I see that listen() is, in fact, called only once and after that only accept() is handling whatever new connection that comes in. Then, how does the server listen the future requests if listen() is never called once we get inside the accept loop.?

                       +----------+
                       |socket()  |
                       +----------+

                       +----------+
                       |bind()    |
                       +----------+

                       +----------+
                       |listen()  |
                       +----------+
                       +----------+
                       |while(1)  |
                       |----------|
                       |accept()  |
                       |          |
                       |          |
                       +----------+

Here's the code I am working on:

/*Server*/

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

int main()
{
    int sockfd, newsock;
    char buffer[200];
    memset(buffer, 0, sizeof(buffer));

    /*Creating a new socket*/
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("Socket error");
        exit(1);
    }

    /*Creating socket address structure*/
    struct sockaddr_in server_addr, client_address;

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(7200);
    server_addr.sin_addr.s_addr = inet_addr("192.168.1.2");
    bzero(&(server_addr.sin_zero),8);

    /*Binding*/
    if(bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        perror("Bind error");
        exit(1);
    }

    /*Listening for new connection; maximum 5*/
    if(listen(sockfd, 5) < 0)
    {
        perror("Listen error");
    }


    while(1)
    {
        socklen_t len = sizeof(client_address);

        if((newsock = accept(sockfd, (struct sockaddr *)&client_address, &len)) < 0)
        {
            perror("Accept error");
            exit(1);
        }

        int i;
        if((i = read(newsock, buffer, sizeof(buffer))) < 0)
        {
            perror("Receive error");
            exit(1);
        }
        if(i == 0)
        {
            printf("Socket closed remotely\n");
        }
        if(i > 0)
        {
            printf("received %d bytes\n",i);
            printf("data: %s",buffer);
        }

        close(newsock);
    }
    close(sockfd);

    return 0;
}
4

4 Answers

7
votes

listen() waits for incoming connections.

That's just a misleading simplification. This system call marks the socket for listening. This means it informs the OS hey, look out for this socket, you're going to receive connection requests on it.

The real waiting function is accept. This function waits for incoming connections. Every time you call it (in a blocking context), one of 2 things happen:

  • If there are unaccepted connections (governed by the second listen argument) accept just fetches one and it's done
  • Otherwise it actually waits for connections to become available before returning
3
votes

The listen() function does not wait for connections; it (a) informs the kernel that the socket will be accepting connections and (b) sets the backlog parameter -- how many connections the kernel will queue up before rejecting new connections. listen() is called only once because it just sets things.

The accept() function is what handles new connections. From the man page:

   The accept() function shall extract the first connection on  the  queue
   of  pending  connections, create a new socket with the same socket type
   protocol and address family as the specified socket, and allocate a new
   file descriptor for that socket.

So, listen() sets up the socket, the kernel handles connection establishment, and accept() pulls connections off the queue and makes them available to your code.

1
votes

A call to listen is just subscribing your process for eventual connections to be delivered later. The actual work is done in kernel-space and a call to accept just obtains a descriptor to a connection that has already been established.

1
votes

listen() puts the socket into a mode for listening. It is forever listening after that, so you don't need to call listen() again. Thereafter, you just call accept() to receive the connections that come in while it is listening, once for each connection.