In socket programming using C language, how to set time limit on server for accepting client to establish connection?

1.2k views Asked by At

I'm developing an application which involves 1 client and 1 server.

I want the server to listen for only 5 seconds for a connection. If the client is not attempting to establish a connection then the sever should stop listening and return an error message. If the client attempts to establish a connection then the server should accept the connection.

The server is listening forever if the client is not making an attempt to establish a connection. I want the server to listen for only 5 seconds, how can this be achieved?

This is the server-side output - server is waiting for client forever:

image

void reception(){
    int sockfd, connfd, len; 
    struct sockaddr_in servaddr, cli; 

    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd == -1) { 
        printf("socket creation failed...\n"); 
        exit(0); 
    } 
    else
        printf("Socket successfully created..\n"); 
    bzero(&servaddr, sizeof(servaddr)); 

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

    if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) { 
        printf("socket bind failed...\n"); 
        exit(0); 
    } 
    else
        printf("Socket successfully binded..\n"); 

    if ((listen(sockfd, 5)) != 0) { 
        printf("Listen failed...\n"); 
        exit(0); 
    } 
    else
        printf("Server listening..\n"); 
    len = sizeof(cli); 

    connfd = accept(sockfd, (SA*)&cli, &len); 

    /*
        Some function should be added at this point to 
        stop the server from listening after 5 seconds

    */
    
    if (connfd < 0) { 
        printf("server acccept failed...\n"); 
        exit(0); 
    } 
    else
        printf("server acccept the client...\n"); 

    receive(connfd); 
    close(sockfd); 

}

1

There are 1 answers

0
Remy Lebeau On

You are using the listening socket in blocking mode, so accept() will block the calling thread until a client connects. To use a timeout, call select() or (e)poll() first to wait for the listening socket to enter a readable state indicating a connection is pending BEFORE you then call accept().

For example:

...

fd_set rds;
FD_ZERO(&rds);
FD_SET(sockfd, &rds);

struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;

int res;
if ((res = select(sockfd+1, &rds, NULL, NULL, &timeout)) <= 0)
{
    if (res < 0)
        printf("select failed...\n"); 
    else
        printf("Time out...\n"); 
    exit(0); 
}

connfd = accept(sockfd, (SA*)&cli, &len); 
...
...

struct pollfd pfd;
pfd.fd = sockfd;
pfd.events = POLLIN;
pfd.revents = 0;

int res;
if ((res = poll(&pfd, 1, 5000)) <= 0)
{
    if (res < 0)
        printf("poll failed...\n"); 
    else
        printf("Time out...\n"); 
    exit(0); 
}

connfd = accept(sockfd, (SA*)&cli, &len); 
...
int epfd = epoll_create(1);
if (epfd < 0)
{
    printf("epoll_create failed...\n"); 
    exit(0);
}

struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = sockfd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) < 0)
{
    printf("epoll_ctl failed...\n"); 
    exit(0);
}

struct epoll_event event;
int res;
if ((res = epoll_wait(epfd, &event, 1, 5000)) <= 0)
{
    if (res < 0)
        printf("epoll_ctl failed...\n"); 
    else
        printf("Time out...\n"); 
    exit(0);
}

close(epfd);

connfd = accept(sockfd, (SA*)&cli, &len); 
...

Either way, note that there is a very small race condition created by these scenarios. If the client connects and then disconnects before accept() is called, accept() may block waiting for a new pending connection, or it may return a valid file descriptor that fails on subsequent I/O calls. There is no guarantee one way or the other.

To solve the race condition, you can either:

  • put the listening socket into non-blocking mode. If select() reports the listening socket has a pending connection, but accept() is not able to accept it right away, then you can act accordingly.

  • use accept() on a listening socket in blocking mode, as you originally were, but use alert() to schedule accept() to be interrupted if it doesn't exit before 5 seconds have elapsed. See this answer for an example.