C FIFO - 如何在等待客户端请求时读取服务器的stdin

时间:2016-04-26 14:43:46

标签: c linux fifo

我正在使用C语言中的linux命名管道实现一个简单的ipc系统。

我有这个服务器代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "external/paths.h"
#include "external/sv.h"
#include "external/tags.h"

int main(int argc, char *argv[])
{
    int fd, bytes_read;
    char request[200];

    // create fifo
    mknod(FIFO_SERVER, S_IFIFO | 0666, 0);

    puts("Servidor initialized.\nWaiting for client requests.");

    // open created fifo
    fd = open(FIFO_SERVER, O_RDONLY);

    while(1)
    {
        if( (bytes_read = read(fd, request, LEN_CL_REQUEST)) == -1 )
            perror("error read()");

        if(bytes_read == 0)
            continue;

        if(bytes_read > 0)
        {
            printf("Request read: %s\n", request);
            // answer back
        }
    } 

    close(fd);
    unlink(FIFO_SERVER);

    return 0;
}

我正在省略客户端,因为我的问题只与服务器有关。通信工作正常,我可以读取客户端的请求,我可以回答它们。现在,假设我想在任何时候都能够在按下键'Q'时退出服务器..我不能这样做因为我的代码在read语句上阻塞等待另一个客户端请求,所以我无法阅读stdin ..

这样的事可能吗?我正在考虑像read语句一样非阻塞,并尝试阅读stdin几秒钟,然后再次检查传入的请求..我一直在搜索,但我还没有找到类似的东西。

更新

我遵循了Jean-BaptisteYuès的方法,但事实证明select只是检测到fifo事件,我真的不知道为什么。

这是我正在测试的代码:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    int result, fd, maxDescriptor;
    char input[20], texto[100];

    mknod("fifo", S_IFIFO | 0666, 0);
    fd = open("fifo", O_RDWR); // RDWR to avoid EOF return to select

    fd_set readset;
    FD_ZERO(&readset);
    FD_SET(fileno(stdin), &readset);
    FD_SET(fd, &readset);

    maxDescriptor = fileno(stdin) > fd ? fileno(stdin) : fd;

    while(1)
    {
        result = select(maxDescriptor+1, &readset, NULL, NULL, NULL);

        if(result == -1)
            perror("select()");
        else if(result)
        {
            puts("data available.");

            if( FD_ISSET(fileno(stdin), &readset) )
            {
                scanf("%s", input);
                printf("%s\n", input);

                if( strcmp(input, "Q") == 0 )
                    break;
            }

            if( FD_ISSET(fd, &readset) )
            {
                read(fd, texto, 100);
                printf("lido:\n%s\n", texto);
            }
        }
        else
            puts("no data.");
    }

    unlink("fifo");
    return 0;
}

更新2:

正如Jean-BaptisteYunès指出的那样,需要重置fd_set,因为它不会自动重置。

这是最终的工作代码:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    int result, fd, maxDescriptor;
    char input[20], texto[100];

    mknod("fifo", S_IFIFO | 0666, 0);
    fd = open("fifo", O_RDWR); // RDWR to avoid EOF return to select

    fd_set readset;
    FD_ZERO(&readset);
    FD_SET(fileno(stdin), &readset);
    FD_SET(fd, &readset);

    maxDescriptor = fileno(stdin) > fd ? fileno(stdin) : fd;

    while(1)
    {
        result = select(maxDescriptor+1, &readset, NULL, NULL, NULL);

        if(result == -1)
            perror("select()");
        else if(result)
        {
            puts("data available.");

            if( FD_ISSET(fileno(stdin), &readset) )
            {
                scanf("%s", input);
                printf("%s\n", input);

                if( strcmp(input, "Q") == 0 )
                    break;
                }

            if( FD_ISSET(fd, &readset) )
            {
                read(fd, texto, 100);
                printf("lido:\n%s\n", texto);
            }

            FD_SET(fileno(stdin), &readset);
            FD_SET(fd, &readset);
        }
        else
            puts("no data.");
    }

    unlink("fifo");
    return 0;
}

1 个答案:

答案 0 :(得分:4)

您必须使用select。你需要在两个通道上等待:来自管道的东西或来自stdin的东西,但你永远不知道要读哪一个。 select的目的是让你的进程在任何通道上等待读或写。

fd_set readset;
FD_ZERO(&readset);        // empty set of descriptor to select on
FD_SET(fd, &readset);     // add the pipe
FD_SET(stdin, &readset);  // add stdin
result = select(fd + 1, &readset, NULL, NULL, NULL); // now wait for something to read on at least one channel (pipe or stdin)
if (result>0) {
    if (FD_ISSET(fd, &readset)) { // test for pipe availability
        // now read the pipe
    }
    if (FD_ISSET(stdin, &readset)) { // test for stdin availability
        // now read stdin
    }
}