Что не так с этим примером ?popen / select?

UPDATE: я обновил код и описание проблемы, чтобы отразить мои изменения.

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

Новый результат – это, конечно, распечатка кода возврата и последней ошибки.

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

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

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

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

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

    Прежде всего, как указывали вы сами и другие, select() действителен только для сокетов под Windows. select() не работает в streamах, что _popen() возвращает _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"); } } 

    Вот и все. Никакой выбор не требуется.

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

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

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

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

    1. select() первым аргументом является дескриптор файла с наивысшим номером в вашем наборе плюс 1. (т.е. output + 1)

      select (output + 1, & fd, NULL, & exceptfds, NULL);

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

      if (FD_ISSET (filePointer, & fd))

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

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

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

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

    Редактировать:

    Очевидно, что в 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. } } 

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