Использование сигнала для прерывания основного потока - PullRequest
0 голосов
/ 29 мая 2020

Я создаю параллельную программу TCP сервер / клиент в C. Каждый раз, когда клиент подключается, основной поток сервера создает новый поток для обработки этого клиента. После этого основной поток возвращается к accept () и остается "зависшим" до тех пор, пока не подключится другой клиент.

Однако я хочу, чтобы клиент мог закрыть сервер. Для этого я полагаю, что мне нужно использовать сигналы. Как я могу подойти к этому. Я новичок в сигналах. Я не знаю, как отправить сигнал в определенный поток c или как я могу использовать их здесь.

Я хочу, чтобы клиент написал '..' и закрыл сервер.

Есть идеи?

Спасибо

1 Ответ

1 голос
/ 30 мая 2020

Хотя я понял ваш вопрос, что меня озадачивает, где вы используете такой сервер ?. В любом случае, я надеюсь, что вы хорошо используете этот сервер.

#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);
    }
}

Терминальная сессия:

Сервер

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

Клиент

$ 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
$

Код закомментирован, и вы можете go через него. Как сказал @MartinJames, вам не нужно беспокоиться о завершении потоков, где это абсолютно не нужно, вам лучше полагаться на процедуры ОС. Поскольку мы обрабатываем сигналы асинхронным способом, в обработчике сигналов можно вызывать только несколько функций (asyn c -signal-safe functions); _exit() - один из них.

...