select () не работает для нескольких именованных каналов - PullRequest
0 голосов
/ 16 апреля 2020

В настоящее время я пытаюсь реализовать чат-сервер / чат с помощью именованных каналов. Сервер принимает имя канала в качестве аргумента командной строки. Затем он приступает к созданию каналов и открывает их для чтения. Другие процессы открывают канал для записи и запрашивают ввод у пользователя (клиент). Как только пользователь нажимает ввод, сообщение записывается в канал. Когда пользователь нажимает ввод, не вводя никаких символов, клиент закрывает канал и завершает работу. Сервер не должен поддерживать переподключения, а клиенты должны подключаться в порядке, указанном в командной строке. Это прекрасно работает только для одного клиента, но не для второго или третьего. Мой код для реализации этого поведения следующий:

Примечание: хештег плюс имя клиента служит для идентификации клиента. Он отправляется вначале для регистрации клиента на сервере, чтобы он знал, с каким клиентом он общается. В файле fifo.h соответственно fifo.c есть мои пользовательские реализации create_fifo, open_fifo и close_fifo, которые обрабатывают создание, открытие и закрытие пафосов.

Редактировать : Теперь я на шаг ближе к тому, чего я должен достичь. Мне пришлось добавить несколько строк в код, который я отмечу в коде ниже, добавив комментарий. Единственная проблема, которая сохраняется, заключается в том, что сервер не распознает, когда второй пользователь покидает сервер первым, а затем первый. Таким образом, он оставляет каналы открытыми и не возвращается к функции clean_up.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <string.h>
#include "fifo.h"

void clean_up(int* fifos, char** argv, int n);

int main(int argc, char** argv){
    int clients = argc - 1;

    int fifos[clients];
    char reg[clients][PIPE_BUF];

    fd_set active_clients;
    FD_ZERO(&active_clients);

    for (int i = 0; i < clients; i++){
        create_fifo(argv[i + 1]);
        fifos[i] = open_fifo(argv[i + 1], O_RDONLY);

        FD_SET(fifos[i], &active_clients);
    }
    fd_set tmpset;
    memcpy(&tmpset, &active_clients, sizeof(fd_set)); // <-- I added this line

    setbuf(stdout, NULL);

    while (clients > 0){
        if (select(fifos[clients - 1] + 1, &active_clients, NULL, NULL, NULL) < 0){
            fprintf(stderr, "Failed to select fifos\n");
            clean_up(fifos, argv, argc);
            return EXIT_FAILURE;
        }

        for (int i = 0; i < clients; i++){

            if (FD_ISSET(fifos[i], &active_clients)){
                char buf[PIPE_BUF];
                read(fifos[i], buf, sizeof(buf));

                if (buf[0] == '#'){
                    strncpy(reg[i], buf + 1, PIPE_BUF);
                    printf("%s joined the server\n", reg[i]);
                    continue;
                }
                if (buf[0] == '\n'){
                    clients--;
                    FD_CLR(fifos[i], &tmpset);
                    FD_CLR(fifos[i], &active_clients);
                    printf("%s left the server\n", reg[i]);
                    continue;
                }
                printf("%s: %s", reg[i], buf);
            }
        }
        memcpy(&active_clients, &tmpset, sizeof(fd_set)); // <-- I added this line
    }
    clean_up(fifos, argv, argc);

    return EXIT_SUCCESS;
}

void clean_up(int* fifos, char** argv, int n){
    for (int i = 1; i < n; i++){
        close(fifos[i - 1]);
        close_fifo(argv[i]);
    }
}

А на стороне клиента реализовано так:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "fifo.h"

int main(int argc, char** argv){
    int fp = open_fifo(argv[argc - 1], O_WRONLY);

    char id[BUF_SIZE] = "";
    char* client_name = argv[argc - 1];

    strncat(id, "#", 1);
    strncat(id, client_name, strlen(client_name));

    write(fp, id, strlen(id) + 1);

    while (1){
        char buf[PIPE_BUF];

        printf("Message: ");
        fgets(buf, PIPE_BUF, stdin);
        write(fp, buf, strlen(buf) + 1);

        if (buf[0] == '\n'){
            break;
        }
    }
    close(fp);

    return EXIT_SUCCESS;
}

Насколько я понял выход, это как-то связано с командой выбора. Он блокируется до тех пор, пока у одного из fifos не будет готов ввод данных, но только для первого клиента, который отправит сообщение. К сожалению, он не разблокируется для других клиентов. Кроме того, я не знаю, является ли мой аргумент, переданный для nfds в команде выбора, правильным. Я также попытался вставить FD_SETSIZE, но это не сильно изменилось. Кто-нибудь знает, что я здесь делаю не так?

...