Search code examples
csocketsserverclientsignals

Using a signal to interrupt the main thread


I'm making a server/client TCP concurrent program in C. Everytime a client connects, the main thread of the server creates a new thread, to handle that client. After that, the main thread goes back to accept(), and stays stuck until another client connects.

However, I want the client to be able to close the server. For that, I assume I have to use signals. How can I approach this. I'm very new to signals. I don't know how to send a signal to a specific thread, or how I can use them here.

I want the client to write '..' and close the server.

Any ideas?

Thanks


Solution

  • Though I understood your question, what puzzles me is, where do you use such a server ?. In any case, I hope you will make a good use of this server.

    #include <signal.h>
    #include <sys/socket.h>
    #include <pthread.h>
    #include <netinet/in.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdio.h>
    
    void sigfunc(int signo)
    {
        _exit(0); // safe to call in signal handler since it is async-signal-safe func
    }
    
    struct client_data {
        int clifd;
        pthread_t ptid;
    };
    
    void* client_func(void* _cli_data)
    {
        struct client_data* cli_data = (struct client_data*) _cli_data;
    
        char buf[100];
    
        for( ; ; ) {
            int rd_status = read(cli_data->clifd, buf, sizeof(buf) - 1);
    
            if (rd_status <= 0)
                break;
    
            buf[rd_status] = '\0';
            printf("%s", buf);
            fflush(stdout);
        }
    
        close(cli_data->clifd);
    
        pthread_kill(cli_data->ptid, SIGRTMIN);
        return NULL;
    }
    
    int main()
    {
        /* Setup a Listening server */
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(struct sockaddr_in));
    
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(8008);
    
        int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
        if (bind(sockfd, (struct sockaddr*) &servaddr, sizeof(struct sockaddr_in)) != 0)
            exit(1);
    
        listen(sockfd, 5);
    
        /* Setup signal handler, use SIGRTMIN as sync signal */
    
        struct sigaction act;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
    
    #ifdef SA_INTERRUPT
        act.sa_flags |= SA_INTERRUPT;
    #endif
    
        act.sa_handler = sigfunc;
    
        sigaction(SIGRTMIN, &act, NULL);
    
        /* Loop and listen for connections */
    
        int addr_len;
    
        for ( ; ; ) {
            struct client_data cli_data;
    
            cli_data.ptid = pthread_self();
            addr_len = sizeof(struct sockaddr_in);
            cli_data.clifd = accept(sockfd, (struct sockaddr*) &servaddr, &addr_len);
    
            if (cli_data.clifd < 0)
                continue;
    
            pthread_t tid;
            pthread_create(&tid, NULL, client_func, &cli_data);
        }
    }
    

    Terminal Session:

    Server

    $ gcc SO.c -lpthread
    $ ./a.out 
    Hi to the server
    Bye to the server
    $
    

    Client

    $ netstat -l | grep 8008 
    tcp        0      0 0.0.0.0:8008            0.0.0.0:*               LISTEN     
    $ nc 127.0.0.1 8008
    Hi to the server
    Bye to the server
    ^C
    $
    

    The code is commented, and you can go through it. As @MartinJames said, you don't need to take a pain of terminating threads where it is absolutely unnecessary, you are better off relying on OS routines. Since we are handling the signals in an asynchronous way, only a few functions can be called in signal handler (async-signal-safe functions); _exit() is one among them.