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). Я не уверен в вашей платформе, поэтому я не могу быть более конкретным относительно кода возврата.
select()
первым аргументом является дескриптор файла с наивысшим номером в вашем наборе плюс 1. (т.е. output + 1)
select (output + 1, & fd, NULL, & exceptfds, NULL);
Первый FD_ISSET(...)
должен быть на fd_set
fd.
if (FD_ISSET (filePointer, & fd))
В вашем 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
.