Что не так с этим примером _popen / select? - PullRequest
3 голосов
/ 30 сентября 2008

ОБНОВЛЕНИЕ: я обновил код и описание проблемы, чтобы отразить мои изменения.

Теперь я знаю, что пытаюсь выполнить операцию Socket на nonsocket. или что мой 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;
}

Теперь новым результатом является распечатка кода возврата и последней ошибки.

Ответы [ 7 ]

3 голосов
/ 30 сентября 2008

У вас есть некоторые данные, готовые для чтения, но вы на самом деле ничего не читаете. При следующем опросе дескриптора данные все равно будут там. Слейте трубку, прежде чем продолжить опрос.

1 голос
/ 02 октября 2008

Прежде всего, как отметили вы и другие, select() действует только для сокетов под Windows. select() не работает с потоками, что _popen() возвращает. Ошибка 10038 четко указывает на это.

Я не понимаю, какова цель вашего примера. Если вы просто хотите порождать процесс и собирать его стандартный вывод, просто сделайте это (что происходит непосредственно со страницы 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");
   }
}

Вот и все. Выбор не требуется.

И я не уверен, как темы помогут вам здесь, это только усложнит вашу проблему.

1 голос
/ 30 сентября 2008

Насколько я могу судить, анонимные каналы Windows нельзя использовать с неблокирующими вызовами, такими как select. Таким образом, хотя ваш _popen и select-код выглядят хорошо независимо, вы не можете соединить их вместе.

Вот аналогичная тема в другом месте.

Возможно, что вызов SetNamedPipeHandleState с флагом PIPE_NOWAIT может работать для вас, но MSDN более чем немного загадочен по этому вопросу.

Итак, я думаю, вам нужно взглянуть на другие способы достижения этого. Я бы предложил иметь чтение в отдельном потоке и использовать нормальный блокирующий ввод / вывод.

0 голосов
/ 01 октября 2008

, поскольку select не работает, я использовал потоки, в частности _beginthread, _beginthreadex.

0 голосов
/ 30 сентября 2008
  1. select() первый аргумент - это дескриптор файла с наибольшим номером в вашем наборе плюс 1. (т.е. вывод + 1)

    выбор (выходной + 1, & fd, NULL, & исключениемfds, NULL);

  2. Первый FD_ISSET(...) должен быть на fd_set fd.

    if (FD_ISSET (filePointer, & fd))

  3. В вашем потоке данных есть данные, и вам нужно прочитать этот поток данных. Используйте fgets (...) или подобное для чтения из источника данных.

    char buf [1024]; ... fgets (buf, sizeof (buf) * sizeof (char), вывод);

0 голосов
/ 30 сентября 2008

Первое, что я заметил неправильно, это то, что вы вызываете FD_ISSET для своих excfds в каждом условном выражении. Я думаю, что вы хотите что-то вроде этого:

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

Поле исключения в select обычно используется для сообщения об ошибках или внеполосных данных в сокете. Когда установлен один из дескрипторов вашего исключения, это не обязательно означает ошибку, а скорее какое-то «сообщение» (т.е. внеполосные данные). Я подозреваю, что для вашего приложения вы, вероятно, можете обойтись без помещения дескриптора файла в набор исключений. Если вы действительно хотите проверить на наличие ошибок, вам нужно проверить возвращаемое значение select и что-то делать, если оно возвращает -1 (или SOCKET_ERROR в Windows). Я не уверен в вашей платформе, поэтому не могу быть более точным в отношении кода возврата.

0 голосов
/ 30 сентября 2008

Первым аргументом для выбора должен быть дескриптор файла с наибольшим номером в любом из трех наборов плюс 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 ().

Вам также необходимо сбрасывать наборы fdsets при каждом вызове select ().

Вам не нужен тайм-аут, поскольку вы его не используете.

Edit:

По-видимому, в Windows nfds игнорируется, но, вероятно, должен быть установлен правильно, просто чтобы код был более переносимым.

Если вы хотите использовать тайм-аут, вам нужно передать его в вызов select как последний аргумент:

// 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.
    }
}
...