Выберите блокировку доступной записи fdset, пока процесс не будет прерван и возобновлен - PullRequest
0 голосов
/ 03 июня 2019

У меня есть приложение на сервере TCP и клиентское приложение TCP. Сервер работает в нескольких потоках, где в одном потоке блокируется вызов select () с неопределенным временем ожидания:

while(running) {
    initFdSets();
    nfds = select(max_fd + 1, &listened_fdset, &write_fdset, &exception_fdset, NULL);
    ...
    reads_from_fds_in_listened_fdset();
    writes_to_fds_in_write_fdset();
}

между тем, в другом потоке в набор fd для записи в первый раз добавляется действительный дескриптор файла сокета:

FD_SET(connection_socket_fd, write_fdset_ptr);    

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

read(sockfd, &msgType, sizeof(int32_t));

и ничего не происходит, пока я не прерву приложение сервера в режиме отладки, а затем возобновлю его. После этого select () возвращает, правильный поток байтов отправляется клиентскому приложению и принимается в соответствии с планом. Writefdset повторно инициализируется перед вызовом select () ... но поведение сервера не меняется в новой итерации while ().

Да, все наборы очищаются с помощью FD_ZERO и повторно инициализируются перед вызовом select () (включая дескриптор файла из 2-го фрагмента).

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

1 Ответ

0 голосов
/ 03 июня 2019

Посетите справочную страницу для выбора здесь: Выберите справочную страницу .

Причина, по которой вы выбираете, зависает, потому что вам нужно передать временной интервал в качестве аргумента, чтобы сообщить емуэто должно быть время ожидания.Процитирую:

   The timeout
       The time structures involved are defined in <sys/time.h> and look
       like

           struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
           };

       and

           struct timespec {
               long    tv_sec;         /* seconds */
               long    tv_nsec;        /* nanoseconds */
           };

       (However, see below on the POSIX.1 versions.)

       Some code calls select() with all three sets empty, nfds zero, and a
       non-NULL timeout as a fairly portable way to sleep with subsecond
       precision.

       On Linux, select() modifies timeout to reflect the amount of time not
       slept; most other implementations do not do this.  (POSIX.1 permits
       either behavior.)  This causes problems both when Linux code which
       reads timeout is ported to other operating systems, and when code is
       ported to Linux that reuses a struct timeval for multiple select()s
       in a loop without reinitializing it.  Consider timeout to be unde‐
       fined after select() returns.

Таким образом, это должно решить вашу проблему:

while(running) {
    timeval tv = {};
    static constexpr long myTimeoutInMicros = 10000; // Put your desired timeout here.
    tv.tv_usec = myTimeoutInMicros;
    tv.tv_sec = 0;
    initFdSets();
    nfds = select(max_fd + 1, &listened_fdset, &write_fdset, &exception_fdset, &tv);
    ...
    reads_from_fds_in_listened_fdset();
    writes_to_fds_in_write_fdset();
}

Я также предлагаю провести дополнительное чтение с помощью руководства по работе с сетями beej: Руководство по работе с сетью Beejs , котороеисчерпывающий источник информации о вызовах через сокеты в стиле bsd.

Редактировать: Стоит отметить, что в Solaris 10 / Solaris 11 (не уверен в IlumO) время выбора истекает независимо от того, предоставлен ли временной интервал.

...