Hatem Mashaqi
Hatem Mashaqi

Reputation: 608

Can an application handle N accept connections and each accept uses an independent thread

I have C linux TCP Client/Server application. I came up with a strange scenario, but I don't know if there are any consequences with this application. I have a server side that can accept N connections, for example this server will accept 100 connections. In this scenario I create the listen socket in the main thread, then I create 100 threads and each thread has an independent accept() and a select() iomux, also each thread can only accept one connection.

My concerns here, if two simultaneous accept() want to accept the same socket(connection) because of the select is a ready to read on the same socket, I don't know if the simultaneous accepts are thread safe in kernel and only one accept can handle that incoming connection and the other will wait for another connection?

I tried that on my RedHat machine that works fine, but I don't know If I am a lucky enough to avoid a deadlock!

Thanks

rc = bind(sd, (struct sockaddr_in *)& groupSock, sizeof(struct sockaddr_in));
    CHECK_VALUE("Bind address error", rc, 0, goto cleanup);

    rc = listen(sd, 10);
    CHECK_VALUE("listen", rc, 0, goto cleanup);

    for(; count< num_socks; count++){

            par_data[count].sd = sd;
            par_data[count].thread_num = count;
            par_data[count].err_chk = -1;

            rc = pthread_create(&thread_id[count], NULL, accept_sock_thread,  (void *)& par_data[count]);
            CHECK_VALUE("pthread_create", rc, 0, goto cleanup);


    }

void * accept_sock_thread(void* atr){

    int                     rc;
    int                     sock            = INVALID_SOCKET;
    int                     datalen         = config.traffic;
    char                    *databuf        = NULL;
    struct thread_data      *data           = NULL;
    struct sockaddr_in      tcp_remote;
    struct timeval          t;
    socklen_t               size;
    fd_set                  socks;

    databuf = malloc(sizeof(char) * datalen);
    memset(databuf, 0, datalen);

    data = (struct thread_data*) atr;
    DEBUG(my_debug_flags, ENTER_FUNCT, ("Enter Function accept_sock_thread thread_num %d \n", data->thread_num));


    FD_ZERO(&socks);
    FD_SET(data->sd, &socks);
    t.tv_sec = 25;
    t.tv_usec = 0;
    rc = select(data->sd + 1 , &socks, NULL, NULL,&t);
    if(rc < 0){
            VL_MISC_ERR(("Error in select with Errno: %d", errno));
            goto cleanup;
    }
    else if(rc == 0){
            VL_MISC_ERR(("Accept Select returned a TIEMOUT."));
            goto cleanup;
    }


    size = sizeof(struct sockaddr_in);
    sock = accept(data->sd, (struct sockaddr *)& tcp_remote, &size);
    CHECK_NOT_EQUAL("tcp accept error", sock, INVALID_SOCKET,  goto cleanup);
cleanup:
    //      sleep(2); /* avoid EOF */
    if(sock != INVALID_SOCKET){

            rc = close(sock);
            if(rc != 0){
                    data->err_chk = -1;
            }
    }
           return NULL;
}

Upvotes: 2

Views: 173

Answers (4)

GreenScape
GreenScape

Reputation: 7717

To use accept() on same socket object from multiple threads/processes is not only standard, but also widly used strategy. From my knowledge apache does this. nginx does this too but in slightly different fashion.

Do note: select() can wake up multiple threads, but only one of them will accept connection, while others will either a) hang in accept() or b) return -1 and set errno to EAGAIN in case of non-blocking IO. Since you are using select() before accept() I suppose socket descriptor is in non blocking mode. So this can lead to some threads never serve a connection.

Also I would advise you to never close same socket in multiple threads. This can lead to very nasty and hard to debug consequences. Either use wrapper and shared_ptr or assign to one of threads a role of 'owner of socket'.

Upvotes: 0

alk
alk

Reputation: 70883

Only one thread would accept(), so far so good ... - but before this all threads will be triggered to have select() return, which might not be what you want.

So if you'd had N threads sleeping in select() and one connection comes in all threads would wake up, but only one would be needed, as only one would succeed in accept()ing.

Upvotes: 0

Duck
Duck

Reputation: 27542

Only one thread is going to accept the connection. The kernel insures this. It has been this way in the unix/posix world for a very long time now.

Upvotes: 1

R&#233;mi Benoit
R&#233;mi Benoit

Reputation: 1346

accept() is thread-safe and reentrant according to POSIX.

This means that two call of accept on the same descriptor should not give an undefined behaviour. One of the accept will open the socket and the other will return an error.

You can see a little more there :

Is accept() thread-safe? Are BSD/Posix sockets reentrant?

Upvotes: 2

Related Questions