Блокировка вызова recv зависает, если сервер не работает - PullRequest
6 голосов
/ 11 февраля 2010

Еще одна проблема с сокетом.

В моем клиентском коде я отправляю некоторый пакет и ожидаю некоторого ответа со стороны сервера:


отправить ()

recv () <- это блокировка </h2> Сразу после send () сервер вылетает и сам перезагружается. Тем временем recv () ждет. Но даже после того, как сервер включен, прием вызова зависает. Я добавил обработку сигнала SIGPIPE, но он все еще не может распознать, что сокет сломан. Когда я отменяю операцию, я получаю сообщение об ошибке от recv (), что было выдано прерывание. Кто-нибудь может мне помочь, как исправить эту ошибку? Это общая библиотека, работающая на компьютере Solaris.

Ответы [ 4 ]

6 голосов
/ 11 февраля 2010

Возможно, вам следует установить задержку тайм-аута для управления этим делом. Это легко сделать, используя setsockopt и установив флаг SO_RECVTIMEO на вашем сокете:

  struct timeval tv;
  tv.tv_sec = 30;
  tv.tv_usec = 0;
  if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,  sizeof tv))
  {
    perror("setsockopt");
    return -1;
  }

Другая возможность - использовать неблокирующие сокеты и управлять чтением / записью с помощью poll (2) или select (2). Вам стоит взглянуть на Руководство Биджа по сетевому программированию .

4 голосов
/ 13 февраля 2010

Как уже упоминалось, вы можете использовать select (), чтобы установить ограничение времени чтения сокета.

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

Ниже приведена функция, которую вы можете использовать, чтобы определить, готов ли сокет к чтению в течение определенного периода времени. Он вернет 1, если в сокете есть данные, доступные для чтения. В противном случае он вернет 0, если истечет время ожидания.

Код основан на примере из книги «Сетевое программирование Unix» (www.unpbook.com), которая может предоставить вам больше информации.

/* Wait for "timeout" seconds for the socket to become readable */
readable_timeout(int sock, int timeout)
{
    struct timeval tv;
    fd_set         rset;
    int            isready;

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

    tv.tv_sec  = timeout;
    tv.tv_usec = 0;

 again:
    isready = select(sock+1, &rset, NULL, NULL, &tv);
    if (isready < 0) {
        if (errno == EINTR) goto again;
        perror("select"); _exit(1);
    }

    return isready;
}

Используйте это так:

if (readable_timeout(sock, 5/*timeout*/)) {
    recv(sock, ...)

Вы упоминаете об обработке SIGPIPE на стороне клиента, что является отдельной проблемой. Если вы получаете это означает, что ваш клиент пишет в сокет, даже после получения RST от сервера. Это отдельная проблема от проблемы с блокирующим вызовом recv ().

Способ, который может возникнуть, состоит в том, что сервер падает и перезагружается, теряя свое состояние TCP. Ваш клиент отправляет данные на сервер, который отправляет обратно RST, так как у него больше нет состояния для соединения. Ваш клиент игнорирует RST и пытается отправить больше данных, и именно этот второй метод send () заставляет вашу программу получать сигнал SIGPIPE.

Какую ошибку вы получили при вызове recv ()?

3 голосов
/ 11 февраля 2010

Проблема в том, что соединение никогда не закрывается. (Пакеты FIN не отправляются и т. Д., Другой конец просто уходит.)

То, что вы хотите сделать, это установить тайм-аут для извлечения на сокете, используя setsockopt (3) с SO_RCVTIMEO в качестве имени_опции.

2 голосов
/ 19 февраля 2010

Другой способ сделать вызов recv () non-blockign в Solaris - это использовать fcntl (), чтобы установить неблокирующий дескриптор сокета:

fcntl (sockDesc, F_SETFL, O_NONBLOCK);

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

...