2016-04-26 63 views
1

我在C.ÇFIFO的 - 如何读取服务器的标准输入,同时等待客户端请求

我有这样的服务器代码,使用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几秒钟,然后再次检查传入的请求..我一直在寻找,但我还没有发现任何类似的东西。

UPDATE

我跟让 - 巴蒂斯特Yunè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:

正如让 - 巴蒂斯特Yunè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; 
} 
+4

选择或投票将让您阻止等待多个文件描述符和解锁时,他们中任何一个准备读或写(查找选择) –

+1

你也可以考虑使用异步I/O编写代码。 “libevent”是一个很好的c库,可以简化编写异步代码。 –

+1

服务器通常无限期地运行服务,不读取标准输入。他们只能读取通讯频道,并且可以通过脚本启动或停止(杀死)。 – kjohri

回答

4

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

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 
    } 
} 
+0

感谢您的回答。一个问题,你传递的socket_fd参数是什么?它不应该是fd + 1吗? – yat0

+0

错误...更正 –

+0

谢谢..但是,我刚刚测试了这种方法,并选择只检测fifo事件..你能检查我的问题编辑? 此外,我已经使用'fileno'函数来获得stdin的整数描述符,因为将'stdin'传递给'FD_SET'和'FD_ISSET'会触发一个错误。 – yat0

相关问题