2008-09-30 86 views
3

更新:我更新了代码和问题描述以反映我的更改。这个_popen/select示例有什么问题?

我现在知道我正在尝试Socket套接字操作的非套接字。或者说我的fd_set是无效的,因为:

select返回-1, WSAGetLastError()收益10038

,但我似乎无法弄清楚它是什么。平台是Windows。我没有发布WSAStartup部分。

int loop = 0; 
FILE *output 

int main() 
{ 
    fd_set fd; 
    output = _popen("tail -f test.txt","r"); 

    while(forceExit == 0) 
    { 
     FD_ZERO(&fd); 
     FD_SET(_fileno(output),&fd); 

     int returncode = select(_fileno(output)+1,&fd,NULL,NULL,NULL); 
     if(returncode == 0) 
     { 
      printf("timed out"); 
     } 
     else if (returncode < 0) 
     { 
      printf("returncode: %d\n",returncode); 
      printf("Last Error: %d\n",WSAGetLastError()); 
     } 
     else 
     { 
      if(FD_ISSET(_fileno(output),&fd)) 
      { 
       if(fgets(buff, sizeof(buff), output) != NULL) 
       {    
        printf("Output: %s\n", buff); 
       } 
      } 
      else 
      { 
       printf("."); 
      } 
     } 
     Sleep(500); 
    } 
    return 0; 
} 

新的结果现在当然是打印出来的返回码和最后一个错误。

回答

0

的第一个参数来选择需要以任何三套编号最高的文件描述符,再加上1:

int select(int nfds, fd_set *readfds, fd_set *writefds, 
       fd_set *exceptfds, struct timeval *timeout); 

另外:

if(FD_ISSET(filePointer,&exceptfds)) 
    { 
      printf("i have data\n"); 
    } 

应该是:

if(FD_ISSET(filePointer,&fd)) 
    { 
      printf("i have data\n"); 
    } 

您应该检查select()的返回码。

您还需要在每次调用select()时重置fdsets。

你不需要超时,因为你没有使用它。

编辑:

显然在Windows上,NFDs的被忽略,但是可能要设置正确,只是让代码更便携。

如果要使用超时,你需要把它传递到选择呼叫作为最后一个参数:

// Reset fd, exceptfds, and timeout before each select()... 
int result = select(maxFDPlusOne, &fd, NULL, &exceptfds, &timeout); 

if (result == 0) 
{ 
    // timeout 
} 
else if (result < 0) 
{ 
    // error 
} 
else 
{ 
    // something happened 
    if (FD_ISSET(filePointer,&fd)) 
    { 
     // Need to read the data, otherwise you'll get notified each time. 
    } 
} 
+0

您对有关第一个参数选择需要正确设置的评论对于Berkeley套接字是正确的。在Windows中不是这样。该参数未使用(虽然它应该仍然是正确设置恕我直言!)。 Poseter没有指定平台,所以我想我会指出平台的不同。平台确实是Windows的 – Mark 2008-09-30 12:49:28

+0

。对不起,没有发布 – SinisterDex 2008-09-30 13:00:48

0

,我发现是错误的第一件事是,你在你的exceptfds调用FD_ISSET在每个条件。我认为,你想是这样的:在选择

if (FD_ISSET(filePointer,&fd)) 
{ 
    printf("i have data\n"); 
} 
else .... 

的领域,除了通常使用的插座上报告错误或出带外数据。当你的例外的描述符被设置时,它不一定意味着错误,而是一些“消息”(即带外数据)。我怀疑对于你的应用程序来说,你可能不会把你的文件描述符放入异常集中。如果你真的想检查错误,你需要检查select的返回值,如果它返回-1(或Windows上的SOCKET_ERROR),则执行一些操作。我不确定你的平台,所以我不能更具体地了解返回码。

3

你有一些数据可供阅读,但你实际上并没有阅读任何东西。当你下次轮询描述符时,数据仍然会在那里。在继续轮询之前排空管道。

0
  1. select()第一个参数是在您所设定的最高数量的文件描述符,加1(即输出+ 1)

    select(输出+ 1,& fd,NULL,& exceptfds,NULL);

  2. 第一个FD_ISSET(...)应该在fd_set fd上。

    如果(FD_ISSET(文件指针,& FD))

  3. 你的数据流中有数据,那么你需要读取数据流。使用fgets(...)或类似的函数从数据源中读取数据。

    char buf [1024]; ... fgets(buf,sizeof(buf)* sizeof(char),output);

+0

更改反映了你的建议,但选择抛出-1。 – SinisterDex 2008-09-30 14:16:25

1

据我所知,Windows匿名管道不能用于像select这样的非阻塞调用。所以,尽管你的_popen和select代码独立地看起来很好,但你不能将它们连接在一起。

Here's a similar thread elsewhere.

这有可能是调用SetNamedPipeHandleState与PIPE_NOWAIT标志可能会为你工作,但MSDN是不是关于这个问题有点神秘了。

所以,我认为你需要看看实现这一目标的其他方法。我建议读一个单独的线程,并使用正常的阻塞I/O。

1

首先,正如你自己和其他人指出的,select()只对Windows下的套接字有效。 select()不适用于_popen()返回的流。 10038错误清楚地标识了这一点。

我不明白你的例子的目的是什么。如果你只是想生成一个进程并收集它的stdout,只是这样做(其中直接来自MSDN _popen页):

int main(void) 
{ 

    char psBuffer[128]; 
    FILE *pPipe; 

    if((pPipe = _popen("tail -f test.txt", "rt")) == NULL) 
     exit(1); 

    /* Read pipe until end of file, or an error occurs. */ 

    while(fgets(psBuffer, 128, pPipe)) 
    { 
     printf(psBuffer); 
    } 


    /* Close pipe and print return value of pPipe. */ 
    if (feof(pPipe)) 
    { 
    printf("\nProcess returned %d\n", _pclose(pPipe)); 
    } 
    else 
    { 
    printf("Error: Failed to read the pipe to the end.\n"); 
    } 
} 

就是这样。不需要选择。

我不确定线程​​是如何帮助你的,这会让你的问题复杂化。