Неожиданные результаты с select и recvfrom - PullRequest
1 голос
/ 18 апреля 2011
fd_set rset;
struct timeval tv;
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
tv.tv_sec = 1;
tv.tv_usec = 0;

for(;;)
{
  for(count = 0; count < elements in sockaddr_in array; count++)
  {
    //flag_array is filled with -1 before for(;;)
    if(flag_array[count] == -1 && select(sockfd+1, &rset, NULL, NULL, &tv))
    {
      recvfrom(...)
    }
    tv.tv_sec = 1;
    FD_ZERO(&rset);//this fixed it
    FD_SET(sockfd, &rset);//and this too
  }

  //contact everyone from sockaddr array (works like a charm!)
}

Если я не отправлю свое сообщение из другой программы в эту программу до того, как произойдет «тайм-аут», оператор выбора «завершится ошибкой», поэтому я не могу использовать внутри него оператор recvfrom. Однажды я сделал так, чтобы моя другая программа связывалась с этой по бесконечному циклу, она никогда не входила в оператор if.

Что работает: Если я связываюсь с этой программой перед каждым тайм-аутом, все в порядке. Если я помещу выражение recvfrom вне if (___ && select), оно будет работать совершенно нормально.

Вот небольшая диаграмма, где эта программа будет называться Recv:

if(A contacts Recv before timeout)    count = 0
   Recv stores contact A in struct
if(B contacts Recv before timeout)    count = 1
   Recv stores contact B in struct
if(timeout)                           count = 2

if(C contacts Recv after timeout)     count = 3
   nothing
                                      count = 4

программа свяжется с А и Б просто отлично // возвращается к началу цикла

flag_array == -1 is false             count = 0
flag_array == -1 is false             count = 1
flag_array == -1 is true...select "fails" count = 2..3..4..(exit loop)

За 2 минуты до публикации я решил еще раз взглянуть на мой предыдущий код. Наверное, я забыл

FD_ZERO(&rset);
FD_SET(sockfd, &rset);

после цикла for (где tv.tv_sec = 1) в.

Может кто-нибудь уточнить, зачем это нужно делать?

Ответы [ 2 ]

4 голосов
/ 18 апреля 2011

select() изменяет переданный fd_set - Вы должны установить его перед каждым вызовом на select().Вот так select() должно работать.

3 голосов
/ 18 апреля 2011

Это необходимо, поскольку select() может изменять наборы дескрипторов файлов.

Ссылаясь на справочную страницу Linux для избранных (3) :

После успешного завершения функция pselect () или select () должна изменить объекты, на которые указывают аргументы readfds, writefds и errorfds, чтобы указать, какие файловые дескрипторы готовы к чтению, готовы к записи или имеют условие ошибки pending, соответственно, и должен возвращать общее количество готовых дескрипторов во всех выходных наборах. Для каждого файлового дескриптора меньше, чем nfds, соответствующий бит должен быть установлен при успешном завершении, если он был установлен на входе, и соответствующее условие истинно для этого файлового дескриптора.

Обратите внимание, что select() может также изменить свой аргумент struct timeval, например, В Linux хранится прошедшее время. Таким образом, вы должны также сбросить все поля tv.

...