LINUX.ORG.RU

KEEPALIVE не работает как ожидается


0

0

Всем привет.
Есть клиентский сокет, в который можно писать запросы и одновременно читать ответы от сервера.

Пытаюсь сделать минимальное время реакции на обрыв соединения.
Устанавливаю опции сокета след. образом:

int val = 1;
int keepidle = 2;
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,(void*)&val,sizeof(val));
setsockopt(fd, SOL_TCP, TCP_KEEPCNT, (void *) &keepidle, sizeof(keepidle));
setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (void*) &keepidle, sizeof (keepidle));
setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (void*) &keepidle, sizeof (keepidle));

Далее происходит блокирующий select() на этом сокете на предмет чтения.
В этот момент выдергиваем Ethernet коннектор из PC, ждем несколько секунд - select выходит с ошибкой timeout

Все как бы хорошо, но если в этот момент ( во время select() ) другой поток отсылает сообщение в сокет серверу,
то timeout не срабатывает вообще. Было так же замечено, что соединение все таки закрывается после ~20мин. ожидания.
Проверялась так же возможность перерывания select на отсылку сообщения и далле переход обратно в select() ( тоесть в одном потоке ) -
поведение аналогичное.

Вопрос: почему такое происходит, как с этим бороться ?
Спасибо.

anonymous

> другой поток отсылает сообщение в сокет серверу, то timeout не срабатывает вообще.

так может другой поток закрывает сокет или получает сообщение об ощибке при попытке сделать send?

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Забыл уточнить, что send() срабатывает без каких либо ошибок и при этом выводит, что количество отосланных байт равно количеству посылаемых.

anonymous
()

sysctl net.ipv4.tcp_keepalive_time

mv ★★★★★
()

tcp_keepalive_time - INTEGER
        How often TCP sends out keepalive messages when keepalive is enabled.
        Default: 2hours.

tcp_keepalive_probes - INTEGER
        How many keepalive probes TCP sends out, until it decides that the
        connection is broken. Default value: 9.

tcp_keepalive_intvl - INTEGER
        How frequently the probes are send out. Multiplied by
        tcp_keepalive_probes it is time to kill not responding connection,
        after probes started. Default value: 75sec i.e. connection
        will be aborted after ~11 minutes of retries.

mv ★★★★★
()
Ответ на: комментарий от mv

Т.е TCP отсылает серию из tcp_keepalive_probes сообщений с интервалом tcp_keepalive_intvl а потом засыпает на tcp_keepalive_time перед отправкой следующей серии. И в худшем случае обрыв может обнаружится только через 2 часа, при значениях по умолчанию.

Я правильно понял?

Vinick ★★
()
Ответ на: комментарий от Vinick

Если соединение простаивает в течение tcp_keepalive_time, то ядро начинает слать tcp_keepalive_probes штук пакетов с интервалом tcp_keepalive_intvl. Т.е. ~ 2 ч + 11 минут.

mv ★★★★★
()
Ответ на: комментарий от Vinick

Да все правильно. Только вот я устанавливаю параметры TCP в меньшие значения, чтобы отловить обрыв.

По этому линку пример программы: http://raven_black.at.tut.by/tcp.c

Если есть время и желание можете проверить - скомпилировать и запустить.

воспроизведение проблемы:

./tcp 20 (через 20 секунд после коннекта будут посланы в сокет данные) после соединения выдергиваем коннектор - select выходит с ошибкой - все нормально

./tcp 3 (через 3 секунд посылается сообщение) выдергиваем коннектор - программа завершается через 20 мин.( почему именно через 20 мин ?)

Насчет /proc/sys/net/ipv4/*

как я понимаю - изменяя эти значение мы делаем данные параметры - параметрами по умолчанию для всех сетевых приложений. Но в данном примере я просто устанавливаю нужные значения для определенного приложения, а не для всей системы вцелом.

anonymous
()
Ответ на: комментарий от anonymous

--- tcp.c.orig	2008-10-30 16:20:18.000000000 +0100
+++ tcp.c	2008-10-30 16:26:15.000000000 +0100
@@ -7,6 +7,8 @@
 #include <string.h>
 #include <netdb.h>
 #include <netinet/tcp.h>
+#include <string.h>
+
 int set_socket_timeout_options(int fd)
 {
     struct timeval tv;
@@ -63,8 +65,8 @@ int socket_blocking_read_write(int socke
 	    char tmp_buffer[4000];
 	    memset(tmp_buffer, 48, sizeof tmp_buffer);
 	    fprintf(stderr, "select timeout \n");
-            int res = send(socket, tmp_buffer, sizeof tmp_buffer, MSG_CONFIRM);
-            fprintf(stderr, "res = %d\n", res);
+            int res = send(socket, tmp_buffer, sizeof tmp_buffer, MSG_CONFIRM | MSG_DONTWAIT);
+            fprintf(stderr, "res = %d (%s)\n", res, strerror(errno));
         }
         else 
         {

mv ★★★★★
()
Ответ на: комментарий от mv

Спасибо!
Это как способ обойти проблему подходит, но тема все равно осталась
открытой.
Не удалось послать сообщение - можно закрыть сокет, и select выйдет с ошибкой, но KEEPALIVE как таковой не сработал ...

см. ниже - патч, в котором данный прием не будет работать

@@ -7,6 +7,8 @@
 #include <string.h>
 #include <netdb.h>
 #include <netinet/tcp.h>
+#include <string.h>
+
 int set_socket_timeout_options(int fd)
 {
     struct timeval tv;
@@ -32,6 +34,7 @@

 int socket_blocking_read_write(int socket, int timeout)
 {
+    int tries = 0;
     int width = socket + 1;
     fd_set read_set;
     do
@@ -63,8 +66,17 @@
            char tmp_buffer[4000];
            memset(tmp_buffer, 48, sizeof tmp_buffer);
            fprintf(stderr, "select timeout \n");
-            int res = send(socket, tmp_buffer, sizeof tmp_buffer, MSG_CONFIRM);
-            fprintf(stderr, "res = %d\n", res);
+            if (tries < 2)
+            {
+                int res = send(socket, tmp_buffer, sizeof tmp_buffer, MSG_CONFIRM | MSG_DONTWAIT);
+                fprintf(stderr, "res = %d\n", res);
+                if ( res < 2 )
+                {
+                    perror("send:");
+                    close(socket);
+                }
+                tries++;
+            }
         }
         else
         {

anonymous
()
Ответ на: комментарий от anonymous

> Спасибо! Это как способ обойти проблему подходит, но тема все равно осталась открытой.

Почему? У тебя программа в сисколле застревает. А keepalive просто убивает "подвисшее" соединение.

> Не удалось послать сообщение - можно закрыть сокет, и select выйдет с ошибкой, но KEEPALIVE как таковой не сработал ...

Почему не сработал?

> см. ниже - патч, в котором данный прием не будет работать

Патч без заголовка. И почему res < 2?

mv ★★★★★
()
Ответ на: комментарий от mv

> Почему? У тебя программа в сисколле застревает. А keepalive просто убивает "подвисшее" соединение.

Потому что в данном случае не сработал send, а должен был по таймауту выйти select. send() не сработал потому что просто не удалось получить подтверждение на отправку.

>> Не удалось послать сообщение - можно закрыть сокет, и select выйдет с ошибкой, но KEEPALIVE как таковой не сработал ...

>Почему не сработал?

KEEPALIVE убивает подвисшее соединение, но в данном случае не он убил select, а соединение было закрыто не успешным send-ом.

>Патч без заголовка. И почему res < 2?

Извиняюсь - res < 0 - опечатка :(

anonymous
()
Ответ на: комментарий от anonymous

> Потому что в данном случае не сработал send, а должен был по таймауту выйти select.

У тебя пример неправильный. После вторая итерация с send делается на закрытом сокете. Я не совсем понимаю, что ты вообще хочешь увидеть?

mv ★★★★★
()

> отсылает сообщение в сокет серверу, то timeout не срабатывает вообще.

Если постоянно что-либо кидать в сокет а удалённая машина не отвечает то в течение минуты сокет сдохнет т.к. не придёт ack на отосланный пакет и ядро сделает connection timed out. Отсюда делаю вывод что где-то у тебя ошибка.

true_admin ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.