Обновлено: Я обновил код и описание проблемы, чтобы отразить мои изменения.
Теперь я знаю, что пытаюсь выполнить операцию 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;
}
Новый результат теперь, конечно же, распечатка кода возврата и последней ошибки.





Первым аргументом для выбора должен быть дескриптор файла с самым большим номером в любом из трех наборов плюс 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.
}
}
платформа действительно Windows. Извините за то, что не опубликовал это
Первое, что я заметил, неправильно, это то, что вы вызываете FD_ISSET на вашем exceptfds в каждом условном выражении. Я думаю, что вам нужно что-то вроде этого:
if (FD_ISSET(filePointer,&fd))
{
printf("i have data\n");
}
else ....
Поле except в select обычно используется для сообщения об ошибках или внеполосных данных в сокете. Когда один из дескрипторов вашего исключения установлен, это не обязательно означает ошибку, а скорее означает некое «сообщение» (т.е. данные вне диапазона). Я подозреваю, что для вашего приложения вы, вероятно, можете обойтись без помещения дескриптора файла в набор исключений. Если вы действительно хотите проверить наличие ошибок, вам нужно проверить возвращаемое значение select и что-то делать, если оно возвращает -1 (или SOCKET_ERROR в Windows). Я не уверен в вашей платформе, поэтому не могу более подробно рассказать о коде возврата.
У вас есть данные, готовые к чтению, но на самом деле вы ничего не читаете. При следующем опросе дескриптора данные все еще будут там. Слейте воду из трубы, прежде чем продолжить опрос.
Первый аргумент select() - это дескриптор файла с наибольшим номером в вашем наборе плюс 1. (т.е. output + 1)
выберите (вывод + 1, & fd, NULL, & exceptfds, NULL);
Первый FD_ISSET(...) должен быть на fd_set fd.
если (FD_ISSET (filePointer, & fd))
В вашем потоке данных есть данные, тогда вам нужно прочитать этот поток данных. Используйте fgets (...) или аналогичный для чтения из источника данных.
char buf [1024]; ... fgets (buf, sizeof (buf) * sizeof (char), output);
изменения отражают ваши предложения, однако select выдает -1.
Насколько я могу судить, анонимные каналы Windows не могут использоваться с неблокирующими вызовами, такими как select. Итак, хотя ваш код _popen и select выглядит хорошо независимо друг от друга, вы не можете объединить их вместе.
Вот похожая ветка в другом месте.
Возможно, вызов SetNamedPipeHandleState с флагом PIPE_NOWAIT может сработать для вас, но MSDN более чем загадочен по этому поводу.
Итак, я думаю, вам нужно искать другие способы достижения этого. Я бы предложил читать в отдельном потоке и использовать обычный блокирующий ввод-вывод.
хорошо .. я пробовал ваш подход безрезультатно. Ты хоть представляешь, как я могу добиться неблокирующей функции?
Вы не можете, AFAICT. Лучше всего просто использовать другой поток для чтения и выполнять обычную блокировку ввода-вывода.
Прежде всего, как вы и другие указали, 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");
}
}
Вот и все. Выбор не требуется.
И я не уверен, как потоки вам здесь помогут, это только усложнит вашу проблему.
Ваш комментарий о том, что первый аргумент для выбора должен быть установлен правильно, верен для сокетов Беркли. в Windows это не так. Параметр не используется (хотя его все равно надо правильно выставлять ИМХО!). Poseter не указал платформу, поэтому я подумал, что укажу на разные платформы.