C: часть кода не выполняется под select () - PullRequest
0 голосов
/ 03 февраля 2010

У меня есть что-то вроде этого:

#define QUIT_TIME 5
int main(int argc, char **argv) {
        //... SOCKETS STUFF ....
    fdmax = parentfd;


    while (notdone) {

        //Set the timers
        waitd.tv_sec = 1;
        waitd.tv_usec = 0;

        FD_ZERO(&tempreadfds);
        FD_ZERO(&tempwritefds);

        FD_ZERO(&readfds);          /* initialize the read fd set */
        FD_ZERO(&writefds);         /* initialize the write fd set */

        FD_SET(parentfd, &readfds); /* add listener socket fd */
        FD_SET(0, &readfds);        /* add stdin fd (0) */

        tempreadfds = readfds; //make a copy
        tempwritefds = writefds; //make a copy

        if (select(fdmax+1, &tempreadfds, &tempwritefds, (fd_set*) 0, &waitd) < 0) {
            error("ERROR in select");
        }

        for(i = 1; i <= fdmax; i++) {

            if(FD_ISSET(i, &readfds)) {
                if(i == parentfd) {
                //This is a new connection
                childfd = accept(parentfd, (struct sockaddr *) &clientaddr, &clientlen);
                if (childfd < 0)
                    error("ERROR on accept");

                InitializeDataStructures(childfd);

                FD_SET(childfd, &readfds); //add to the master set
                if(childfd > fdmax)
                    fdmax = childfd;
            } else {
                //Existing connection
                if((nBytes = read(i, connections[i].buffer, MAXBUFFER)) <= 0) {
                    if(nBytes == 0) {
                        //Connection closed
                        printf("Socket %d hung up\n", read_write_loop);
                    } else {
                        error("\nReceive error\n");
                    }

                    FD_CLR(i, &readfds);
                } else {
                    //We have some data from the connection
                                            //... Manipulate the buffer
                    //Handle the message
                }
            }
            }

            if(FD_ISSET(i, &writefds)) {
                                .....
                FD_CLR(i, &writefds);
            }

            //Timer checking
            if(connections[i].active) {
                gettimeofday(&TimeNow, NULL);
                timeval_diff(&Interval, &TimeNow, &connections[i].TimeConnected);
                printf("*_*_*__*_*_*__*_*_*_*_* difference is %ld seconds, %ld microseconds\n",
                         Interval.tv_sec,
                         Interval.tv_usec
                        );
                if(Interval.tv_sec >= QUIT_TIME) {
                    printf("Timer elapsed!!\n");
                }
            }

        }
    }

    /* clean up */
    printf("Terminating server.\n");
    close(parentfd);
    return 0;
}

void InitializeDataStructures(int i) {

    clients[i].active = YES;
    clients[i].fd = i;
    //Initialize other members of the structure
}

long long timeval_diff(struct timeval *difference, timeval *end_time, struct timeval *start_time) {
      struct timeval temp_diff;

      if(difference==NULL)
        difference=&temp_diff;

      difference->tv_sec =end_time->tv_sec -start_time->tv_sec ;
      difference->tv_usec=end_time->tv_usec-start_time->tv_usec;

      while(difference->tv_usec<0)
      {
        difference->tv_usec+=1000000;
        difference->tv_sec -=1;
      }

      return 1000000LL*difference->tv_sec + difference->tv_usec;

    }

Я ожидал, что строка «Таймер истек» будет напечатана хотя бы один раз (TimeConnected был инициализирован в одно из условий if) во время выполнения,но по какой-то причине он никогда не распечатывается.Я думал, что мой цикл while должен продолжать печатать его ... Кто-нибудь знает, если я что-то напутал?

РЕДАКТИРОВАТЬ: На самом деле я использую таймер, чтобы отключить время после тайм-аута.Я только что заметил, что он печатает «Таймер истек», если другой клиент подключается к серверу.Я передал последний параметр для выбора, но не уверен, почему он не оказывает никакого влияния.

Благодаря bdk !!Если вам интересно знать «глупую» ошибку, которая была у меня в этом коде, прочитайте подробное обсуждение ниже ... Это была очевидная ошибка, которую я упустил из виду ... все из-за одного предложения в уроках: "select modizesваши оригинальные дескрипторы ".

Список изменений:

  • Обратите внимание, что набор операторов FD_ZERO был ошибочно помещен в цикл while
  • FD_ISSET передается readfdsи writefds вместо tempreadfds и tempwritefds ...

РАБОЧИЙ КОД:

#define QUIT_TIME 5
int main(int argc, char **argv) {
        //... SOCKETS STUFF ....
    fdmax = parentfd;


    FD_ZERO(&readfds);          /* initialize the read fd set */
    FD_ZERO(&writefds);         /* initialize the write fd set */

    while (notdone) {

        //Set the timers
        waitd.tv_sec = 1;
        waitd.tv_usec = 0;

        FD_ZERO(&tempreadfds);
        FD_ZERO(&tempwritefds);


        FD_SET(parentfd, &readfds); /* add listener socket fd */
        FD_SET(0, &readfds);        /* add stdin fd (0) */

        tempreadfds = readfds; //make a copy
        tempwritefds = writefds; //make a copy

        if (select(fdmax+1, &tempreadfds, &tempwritefds, (fd_set*) 0, &waitd) < 0) {
            error("ERROR in select");
        }

        for(i = 1; i <= fdmax; i++) {

            if(FD_ISSET(i, &tempreadfds)) {
                if(i == parentfd) {
                //This is a new connection
                childfd = accept(parentfd, (struct sockaddr *) &clientaddr, &clientlen);
                if (childfd < 0)
                    error("ERROR on accept");

                InitializeDataStructures(childfd);

                FD_SET(childfd, &readfds); //add to the master set
                if(childfd > fdmax)
                    fdmax = childfd;
            } else {
                //Existing connection
                if((nBytes = read(i, connections[i].buffer, MAXBUFFER)) <= 0) {
                    if(nBytes == 0) {
                        //Connection closed
                        printf("Socket %d hung up\n", read_write_loop);
                    } else {
                        error("\nReceive error\n");
                    }

                    FD_CLR(i, &readfds);
                } else {
                    //We have some data from the connection
                                            //... Manipulate the buffer
                    //Handle the message
                }
            }
            }

            if(FD_ISSET(i, &tempwritefds)) {
                                .....
                FD_CLR(i, &writefds);
            }

            //Timer checking
            if(connections[i].active) {
                gettimeofday(&TimeNow, NULL);
                timeval_diff(&Interval, &TimeNow, &connections[i].TimeConnected);
                printf("*_*_*__*_*_*__*_*_*_*_* difference is %ld seconds, %ld microseconds\n",
                         Interval.tv_sec,
                         Interval.tv_usec
                        );
                if(Interval.tv_sec >= QUIT_TIME) {
                    printf("Timer elapsed!!\n");
                }
            }

        }
    }

    /* clean up */
    printf("Terminating server.\n");
    close(parentfd);
    return 0;
}

void InitializeDataStructures(int i) {

    clients[i].active = YES;
    clients[i].fd = i;
    //Initialize other members of the structure
}

long long timeval_diff(struct timeval *difference, timeval *end_time, struct timeval *start_time) {
      struct timeval temp_diff;

      if(difference==NULL)
        difference=&temp_diff;

      difference->tv_sec =end_time->tv_sec -start_time->tv_sec ;
      difference->tv_usec=end_time->tv_usec-start_time->tv_usec;

      while(difference->tv_usec<0)
      {
        difference->tv_usec+=1000000;
        difference->tv_sec -=1;
      }

      return 1000000LL*difference->tv_sec + difference->tv_usec;

    }

Ответы [ 3 ]

1 голос
/ 03 февраля 2010

Если я правильно читаю ваш код, вы вводите цикл while, а затем проверяете, находится ли дескриптор в наборе чтения. Если это так, то вы переходите к части accept () вашего оператора if. Если это не так, то вы вводите остальную часть, где вы сразу же блокируете чтение. Если сокет активен, но данных нет, вы будете блокировать его до тех пор, пока данные не станут доступны. Он не перейдет в раздел, где он даже проверяет таймер, пока не получит успешное чтение или ошибку на входе.

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

Обычно вы создаете один fdset для проверки сокетов, для которых вы принимаете соединения, и другой для тех, которые вы приняли и на самом деле читаете данные. Я полагаю, вы можете сделать это так, как вы представляли, но я бы посоветовал вам переключиться с read () на recv () и использовать флаг MSG_PEEK.

1 голос
/ 03 февраля 2010

Посмотрите на параметры выбранного цикла, они мне кажутся подозрительными. Главным образом в том, что вы вызываете select для tempreadfd и tempwritefd, но затем, когда вы вызываете FD_ISSET, вы передаете ему readfd и writefd. перед вызовом select вы используете FD_SET, чтобы установить все интересующие вас fd. Поскольку эти переменные не отправляются на выбор, fd, которые не сработали, не маскируются. Следовательно, вы обнаруживаете «активность» во всех ваших дескрипторах. Этот дескриптор принятия действительно не выполняет никаких действий, поэтому он блокируется, пока не подключится новый клиент.

Это мое предположение, по крайней мере.

0 голосов
/ 03 февраля 2010

Это очень специфичная для кода проблема, но кто знает ... может быть, это поможет некоторым другим ... Я решил использовать поток для решения проблемы (я знаю ... не самый лучший, но я просто схожу с ума, пытаясь отладить это ...). Спасибо всем, у кого хватило терпения помочь мне ...

В приведенном выше коде вместо тестирования таймера внутри цикла while в main () я изменил его следующим образом:

if(ThreadSpawned == 0) {
                pthread_create(&thread, NULL, cleanup, (void *) &fdmax);
                ThreadSpawned = 1;
            }

И тогда функция очистки выглядит следующим образом:

void *cleanup(void *arg) {
    //Timer checking
    int i, *fdmax;
    fdmax = (int *) arg;

    while(1) {
        for(i = 1; i <= *fdmax; i++) {
            if(connections[i].active == 1) {
                gettimeofday(&TimeNow, NULL);
                timeval_diff(&Interval, &TimeNow, &clients[i].TimeConnected);
                printf("*_*_*__*_*_*__*_*_*_*_* difference is %ld seconds, %ld microseconds\n",
                        Interval.tv_sec,
                        Interval.tv_usec
                );
                fflush(stdout);
                if((int) Interval.tv_sec >= QUIT_TIME) {
                    printf("Timer elapsed!!\n");
                    fflush(stdout);
                }
            }
        }
        sleep(20);
    }
}

Любые более чистые решения приветствуются. Возможно, причина, предложенная bdk в одном из его ответов, заключается в том, что программа блокирует команду accept, но тогда, если она там блокируется, у меня не будет возможности рассчитать тайм-аут ... Поэтому я закончил тем, что использовал поток для выполнить эту задачу ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...