0

I am working on a program using threads to treat demands of remote clients in C++. The server will wait for clients to connect, and launch a thread doing some things.
For the server to shut down, the user must do an external interrupt Crtl+C, and the code will handle the signal (using <csignal>) in order to shut everything properly.

The waiting itself is made with a while loop, the server waits for a connection, and launches the thread. It's during this loop that I want to handle the signal. Here it is:

std::signal(SIGINT, interruptHandler);
while(intStatus != 2){
        ClientSocket client;
        client.csock = accept(sock,reinterpret_cast<SOCKADDR*>(&(client.csin)),&(client.crecsize));

        tArray.push_back(new pthread_t);
        pthread_create(tArray.back(), NULL, session, &client);
}
std::cout<<"Waiting for Clients to exit..."<<std::endl;
for(pthread_t* thread : tArray)
        pthread_join(*thread,NULL);
  • std::sig_atomic_t intStatus is a global variable edited by interruptHandler when it's called.
  • ClientSocket is a struct containing informations about the client:
    • csoc the socket of the client
    • csin the adress of the socket
    • crecsize gives the size of the data to send (I think)
  • I am using <winsock2.h> to connect between server and clients. sock is the socket of the server.
  • I am using <pthread.h> to manage threads. They are stored in std::list<pthread_t*> tArray.

The problem is : because accept() pauses the process, and of course the loop must end to see if Crtl+C has been sent, the server can only shut down when an additional client connects.

Is there a way for interruptHandler() to break the while and let the program go ahead with the for loop and next? Should I change the algorithm? On a last resort I know we can set up tags in Assembly for the program to jump to. Is there a way to do this in C++ too?

By the way, I am using <pthread.h> and <winsock2.h> because I have too (it's a student project). Thanks for helping.

drescherjm
  • 10,365
  • 5
  • 44
  • 64
julian_mzt
  • 13
  • 4
  • Related, if not a duplicate: https://stackoverflow.com/questions/12861956/is-it-possible-and-safe-to-make-an-accepting-socket-non-blocking – fabian Oct 27 '22 at 19:05

2 Answers2

1

The problem is : because accept() pauses the process, and of course the loop must end to see if Crtl+C has been sent, the server can only shut down when an additional client connects.

accept blocks, that is true, but you can use select to check if you have incoming connections, guaranteeing that if you call accept at that point you won't block.

Blindy
  • 65,249
  • 10
  • 91
  • 131
0

Thanks to @Blindy, I digged up about select(2) and came up with something. I will try to explain it here instead of marking @Blindy's answer as solution, since it took me some time to make it work and I want to save people from the headaches I had.

Here is the new while loop, using select() :

#include <csignal>
#include <sys/time.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>  
#include <sys/ioctl.h>   //On Unix (with some other includes)

...

    //The Server will only shut down if CRTL+C is sent
    std::signal(SIGINT, interruptHandler);

    //Make the socket non-blocking
    int success = 1;
    int nonBlocking = 1;
    //If success = -1 : Error in ioctlsocket(), if success = 1 : ioctlsocket() wasn't called
    //Optional condition, for portability sake.
    #if defined (WIN32)
        //if on Windows
        success = ioctlsocket(sock,FIONBIO,&nonBlocking);
    #else
        //if on Unix (or else)
        success = ioctl(sock,FIONBIO,&nonBlocking);
    #endif
    if(success != 0) throw std::runtime_error("ioctlsocket() failed, code : "+std::to_string(success));                    

    //Declaration of variables needed for select() :
    fd_set readSet;   //A set of Sockets used to call listen() - here there is only one
    timeval inter;    //The time interval during select() is waiting for a pending connection

    int res;          //Declaration of the variable storing select()'s return
    while(intStatus != 2){       
        //Reseting readSet
        FD_ZERO(&readSet);
        FD_SET(sock, &readSet);
        //Reseting inter, select() modifying it on Unix
        inter = {0,50};         //The select() will wait 50ms before retrying

        res = select(4097,&readSet,NULL,NULL,&inter);
        if(res == -1){                       //If select() did not finish
            if(errno == EINTR)  continue;    //If it was because of CRTL+C
            else                throw std::runtime_error("Select() failed.");
        } 
        if(res == 0)            continue;    //If no connections are pending

        //If the new readSet modified by select() still has the socket in it
        if(!FD_ISSET(sock,&readSet))    throw std::runtime_error("The socket is no longer in readfds");

        ClientSocket client;
        client.sock = accept(sock,reinterpret_cast<SOCKADDR*>(&(client.sin)),&(client.recsize));

        tArray.push_back(new pthread_t);
        pthread_create(tArray.back(), NULL, session, &client);

    }

I hope it's somewhat clear and I didn't do mistakes. Tell me if something is wrong, I will edit it. Thanks again, Blindy !

julian_mzt
  • 13
  • 4