while (xxx) {
timeout.tv_sec=TIMEOUT;
timeout.tv_usec=0;
FD_ZERO(&set);
FD_SET(sd,&set);
switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
xxxxx
}
работает нормально, однако
FD_ZERO(&set);
FD_SET(sd,&set);
while (xxx) {
timeout.tv_sec=TIMEOUT;
timeout.tv_usec=0;
switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
xxxxx
}
нет. Он работает в первый раз, но в следующий раз, когда он проходит через цикл while, он получает тайм-аут, даже если sd-сокет получает данные. Мне кажется пустой тратой ресурсов каждый раз опорожнять и заполнять сет.
У кого-нибудь есть хорошее объяснение, почему это так, и даже лучше, возможно, предложение, как этого избежать?





Прочтите избранную страницу руководства. Возвращенный набор - это только дескрипторы файлов, которые готовы к использованию. Вы должны использовать FD_ISSET для проверки каждого, установлен он или нет.
Всегда инициализируйте fd_set прямо перед его использованием.
Я знаю о FD_ISSET, но я еще не включил его, потому что на данный момент я указываю только один сокет (позже я добавлю другие сокеты). Итак, теперь есть способ «повторно использовать» без предварительной установки fd_set?
Задокументированное поведение select () включает изменение наборов на месте. По словам Прагматичного программиста, «select не сломан».
Так работает select. Он работает лучше всего и имеет больше смысла, если у вас более одного сокета. В том-то и дело: вы выбираете из множества сокетов. Если вы хотите читать из одного сокета, просто прочтите или получите его.
Чтение не предлагает тайм-аут. Следовательно, использование select. + тот факт, что я добавлю второй сокет позже на стадии разработки.
Просто чтобы предлагать альтернативы: если sd - это сокет, вы можете использовать setsockopt (sd, SO_RCVTIMEO, ...), чтобы добавить таймаут чтения. Однако, если вы собираетесь добавить второй сокет позже, выберите лучший вариант.
select изменяет свои аргументы. Вам действительно нужно каждый раз заново инициализировать его.
Если вас беспокоят накладные расходы, стоимость обработки полного FD_SET в ядре несколько более значительна, чем стоимость FD_ZERO. Вам нужно передать только свой максимальный fd, а не FD_SETSZIZE, чтобы минимизировать обработку ядра. В вашем примере:
switch (select((sd + 1),&set,NULL,NULL,&timeout))
В более сложном случае с несколькими fd вы обычно в конечном итоге поддерживаете максимальную переменную:
FD_SET(sd,&set);
if (sd > max) max = sd;
... repeat many times...
switch (select((max + 1),&set,NULL,NULL,&timeout))
Если у вас будет большое количество файловых дескрипторов и вы беспокоитесь о накладных расходах, связанных с их обработкой, вам следует рассмотреть некоторые альтернативы select (). Вы не упоминаете ОС, которую используете, но для Unix-подобных ОС их несколько:
API-интерфейсы разные, но все они, по сути, представляют собой интерфейс ядра с отслеживанием состояния для поддержки набора активных описаний файлов. Как только fd добавлен в набор, вы будете получать уведомления о событиях на этом fd без необходимости постоянно передавать его снова.
В качестве альтернативы select () почему бы не использовать poll (), который, в отличие от упомянутых вами, одинаков во многих Unix?
poll () лучше тем, что вам не нужно каждый раз повторно инициализировать его массив, но вы по-прежнему копируете большую структуру в ядро и из него при каждом вызове. Если у вас много fd, другие альтернативы позволяют избежать этих постоянных накладных расходов, изменяя набор fd в ядре.
Я думаю, что часть кода, который вы извлекли, может быть важна для понимания того, почему это работает именно так.