当作家来来去去时,从命名管道重新读取

问题描述:

我遇到了一个问题,我必须从命名管道读取数据。我必须处理写作者到命名管道的情况,但我需要在整个应用程序中保持相同的管道畅通。当作家来来去去时,从命名管道重新读取

我总结了下面的代码。

int main(int c, char *v[]) 
{ 
    int rfd; 
    if ((rfd = open(PIPENAME, O_RDONLY | O_NONBLOCK)) < 0) 
    { 
     perror("open"); 
     return 1; 
    } 

    char buffer[ 1024000 ]; 

    // used to give select an upper bound on number of fds 
    int nfd = rfd + 1; 

    fd_set rfds; 
    FD_ZERO(&rfds); 
    FD_SET(rfd, &rfds); 

    while(true) 
    { 
     int nr = select(nfd, &rfds, NULL, NULL, NULL); 

     if (nr < 0) 
     { 
      perror("select"); 
      break; 
     } 

     if (FD_ISSET(rfd, &rfds)) 
     { 
      //std::cout << "RFD SET" << std::endl; 
      // Ok, we have data we can read 
      int nread = read(rfd, buffer, sizeof(buffer)); 
      if (nread < 0) 
      { 
       perror("read"); 
       break; 
      } 
      else if (nread == 0) 
      { 
       std::cout << "read 0" << std::endl; 
      } 
      else 
      { 
       std::cout << "read " << nread << " bytes" << std::endl; 

      } 

     } 
    } 

    close(rfd); 

    return 0; 
} 

我的问题是,第一进程写入命名管道和断开(关闭)后,它的结束,我的程序不会在选择阻塞。它实际上具有rfd集合,并且读取返回零字节,在紧密循环中读取。

我需要rfd处于NON_BLOCKING模式,否则打开将会阻塞,直到出现一个编辑器。

我试过用fcntl设置为BLOCKING模式,但那也不管用。

我对管道语义的有限理解让我觉得我需要清除管道上的EOF状态,以便选择现在会阻塞。但是,我不知道如何做到这一点。

我把自己放在你的集体智慧:) 马克。

好的,我想出了一个解决方案,但我并不满意。如果你问我,这有点像是“砸碎坚果的锤子”。

..... 
else if (nread == 0) 
{ 
    std::cout << "read 0" << std::endl; 

    FD_ZERO(&rfds); 
    close(rfd); 
    if ((rfd = open(PIPENAME, O_RDONLY | O_NONBLOCK)) < 0) 
    { 
     perror("re-open"); 
     break; 
    } 
    FD_SET(rfd, &rfds); 
    nfd = std::max(wfd, rfd) + 1; 
} 
else 
..... 

基本上,我关闭并重新打开管道。

我仍然欢迎更好的解决方案。

+1

关闭并重新打开该管道是正确的,唯一的解决办法,如果你想继续使用命名管道。这听起来像UNIX域套接字可能实际上更适合您的应用程序,但(它们也被文件系统中的路径名称引用,但使用BSD套接字API)。 – caf 2010-01-19 10:54:37

+0

我尝试过使用unix套接字,但不能简单地使用“cat filename> named_pipe”。您必须编写一个专用应用程序以“套接字”方式格式化输入到您的应用程序 – ScaryAardvark 2010-01-19 14:40:33

您是否试过在非阻塞模式下打开fds,当您的read()返回EWOULDBLOCK/EAGAIN时,在fd?上做clearerr()

+0

clearerr仅适用于FILE *而非fd。 – ScaryAardvark 2010-01-19 10:05:41

+0

是的。我能看到的唯一方法是'fdopen'为fd获得'FILE *',然后在''上使用'clearerr()'。 – 2010-01-19 10:08:45

+0

问题是,虽然你永远不能关闭FILE *,因为那会关闭底层fd。我上面的例子假设我打开了管道,但实际上我希望解决方案即使交给管道fd也能工作。最终在这些情况下,使用fdopen/clearerr会导致内存泄漏,因为您不知道FILE *传递给您的方式,您既不能释放()也不能将其删除。 – ScaryAardvark 2010-01-19 10:16:12

找到一些其他职位:

if ((rfd = open(PIPENAME, O_RDWR | O_NONBLOCK)) < 0) 

即开RDWR而不是RDONLY似乎工作...

+0

只是这个选项没有遵守“数据包”边界(我不知道RDONLY是否会这样做,也就是说,一次可以读取多个写入 - 例如,如果读取大小大约是写入数据包的1.5倍大小... – user2220505 2015-02-05 12:08:29