select()不适用于多个命名管道

时间:2020-04-16 18:05:01

标签: c pipe ipc mkfifo

我目前正在尝试通过命名管道来实现聊天服务器/ shoutbox。服务器将管道的名称作为命令行参数。然后,它继续创建管道并打开它们以进行读取。其他过程打开管道进行写入,并要求用户输入(客户端)。用户点击回车后,消息将被写入管道。当用户按Enter键而不输入任何字符时,客户端将关闭管道并退出。服务器不需要支持重新连接,而客户端则需要按照在命令行上指定的顺序进行连接。这仅适用于一个客户端,但不适用于第二个或第三个客户端。我实现此行为的代码如下:

旁注:井号和客户名称用于标识客户。它从一开始就发送以在服务器上注册客户端,以便它知道与哪个客户端通信。在文件fifo.hfifo.c中分别有我的create_fifoopen_fifoclose_fifo的自定义实现,它们处理fifo的创建,打开和关闭。 / p>

编辑:现在,我距离要实现的目标又近了一步。我必须在代码中添加几行,然后通过添加注释在下面的代码中进行标记。唯一仍然存在的问题是,第二个用户第一次离开服务器时服务器无法识别,而第一个用户离开时服务器无法识别。因此它将管道保持打开状态,并且不会返回到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;
}

据我了解,它与select命令有关。它会阻塞,直到其中一个fifos准备好一些输入为止,但仅适用于发送消息的第一个客户端。不幸的是,它不会为其他客户端解除阻塞。另外,我不知道在select命令中为nfds传递的参数是否正确。我也确实尝试放入FD_SETSIZE,但并没有太大变化。有人知道我在这里做错了吗?

0 个答案:

没有答案