Search code examples
socketsselecttcpserver

Questions about select()


Considering the following class method:

void TCPServer::listenWithTimeout() {
  fd_set descrSet;
  struct timeval timeout = {1, 0};
  while (listeningThread.active) {
    std::cout << "TCP Server thread is listening..." << std::endl;
    FD_ZERO(&descrSet);
    FD_SET(mySocketFD, &descrSet);
    int descrAmount = select(mySocketFD + 1, &descrSet, NULL, NULL, &timeout);
    if (descrAmount > 0) {
      assert(FD_ISSET(mySocketFD, &descrSet));
      int threadIndx = findAvailableThread();
      if (threadIndx < 0) {
        std::cout << "Connection not accepted: no threads available..." << std::endl;
      } else {
        joinThread(threadIndx);
        int newSocketFD = accept(mySocketFD, (struct sockaddr *) &mySocket, &clieAddrssLen);
        std::cout << "TCP Client connected..." << std::endl;
        connectionThreads[threadIndx].thread = std::thread(TCPServer::startTCPConnectionWithException, std::ref(*this), threadIndx, newSocketFD);
        connectionThreads[threadIndx].active = true;
      }
    }
  }
  std::cout << "TCP Server thread is terminating..." << std::endl;
}

Here are some question:

  1. when there are not available threads (findAvailableThreads() returns -1), is it a normal behaviour that select() doesn't wait its timeout and so the while loop iterates really fast until a new thread is available?

  2. if yes, how could I avoid these really fast iterations? Instead of using something like a simple sleep() at line 13 inside the if branch, is there a way to let select() restore its timeout? Or even, is there a way to completely reject the incoming connection pending?


Solution

  • when there are not available threads (findAvailableThreads() returns -1), is it a normal behaviour that select() doesn't wait its timeout and so the while loop iterates really fast until a new thread is available?

    Yes, because under that condition, you are not calling accept(), so you are not changing the listening socket's state. It will remain in a readable state as long as it has a client connection waiting to be accept()'ed.

    if yes, how could I avoid these really fast iterations?

    Call accept() before checking for an available thread. If no thread is available, close the accepted connection.

    Instead of using something like a simple sleep() at line 13, inside the if branch, is there a way to let select() restore its timeout?

    The only way is to accept() the connection that put the listening socket into a readable state, so it has a chance to go back to a non-readable state. The timeout will not apply again until the socket is no longer in a readable state.

    Or even, is there a way to completely reject the incoming connection pending?

    The only way is to accept() it first, then you can close() it if needed.

    Try this:

    void TCPServer::listenWithTimeout() {
        fd_set descrSet;
        while (listeningThread.active) {
            std::cout << "TCP Server thread is listening..." << std::endl;
            FD_ZERO(&descrSet);
            FD_SET(mySocketFD, &descrSet);
            struct timeval timeout = {1, 0};
            int descrAmount = select(mySocketFD + 1, &descrSet, NULL, NULL, &timeout);
            if (descrAmount > 0) {
                assert(FD_ISSET(mySocketFD, &descrSet));
                int newSocketFD = accept(mySocketFD, (struct sockaddr *) &mySocket, &clieAddrssLen);
                if (newSocketFD != -1) {
                    int threadIndx = findAvailableThread();
                    if (threadIndx < 0) {
                        close(newSocketFD);
                        std::cout << "Connection not accepted: no threads available..." << std::endl;
                    } else {
                        joinThread(threadIndx);
                        std::cout << "TCP Client connected..." << std::endl;
                        connectionThreads[threadIndx].thread = std::thread(TCPServer::startTCPConnectionWithException, std::ref(*this), threadIndx, newSocketFD);
                        connectionThreads[threadIndx].active = true;
                    }
                }
            }
        }
        std::cout << "TCP Server thread is terminating..." << std::endl;
    }