LINUX.ORG.RU

быстрый detect закрытия соединения

 


1

2

пишу программу которая коннетится куда то. положим к 127.0.0.1:22 ожидает события через poll() (epoll тут избыточен) и читает если есть что читать.

так вот коннектится она к ssh, вычитывает приглашение от ssh и засыпает на poll(), в это время я гашу ssh, а прога детектирует закрытие канала только может через минуту. как ускорить детектирование закрытие соединения?

пробывал уже юзать

struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
rc = setsockopt(socket_handle, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));

int xtimeout = 10000;  // user timeout in milliseconds [ms]
rc = setsockopt(socket_handle, SOL_TCP, TCP_USER_TIMEOUT, (char*)&xtimeout, sizeof(xtimeout));

эффекта нет. кроме как через sysctl и настройку tcp стека можно из программы это затюнить?

★★


fd = accept(listenSocket);
pid_t childProcess = fork();
if (childProcess == (pid_t)-1) {
	perror("Unable to create new process for client connection");
	exit(1);
}
else if (childProcess == 0) {
	// read from socket, process queries, etc.
}
else {
	// use the poll system call to be notified about socket status changes
	struct pollfd pfd;
	pfd.fd = fd;
	pfd.events = POLLIN | POLLHUP | POLLRDNORM;
	pfd.revents = 0;
	while (pfd.revents == 0) {
		// call poll with a timeout of 100 ms
		if (poll(&pfd, 1, 100) > 0) {
			// if result > 0, this means that there is either data available on the
			// socket, or the socket has been closed
			char buffer[32];
			if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
				// if recv returns zero, that means the connection has been closed:
				// kill the child process
				kill(childProcess, SIGKILL);
				waitpid(childProcess, &status, WNOHANG);
				close(fd);
				// do something else, e.g. go on vacation
			}
		}
	}
}
anonymous
()
Ответ на: комментарий от anonymous

это все не то. в моем случае poll не получает никаких событий пока сокет не поймет что порта уже нет. это нужно как то ускорить (если возможно).

quester ★★
() автор топика

Попробуй ещё включить keepalive и уменьшить интервал. Как-то так:

int interval = 2;
int keepalive = 1;
setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
setsockopt(socket_handle, SOL_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));
i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Чтоб два раза не вставать.

Использовал я ices, а затем libshout, чтобы лить поток на icecast. Так вот, было это давно очень, поэтому никаких тестов я тогда не заводил, ибо потыкать просто было интересно. Но, что ices, что самопальный стример на libshout — сегфолтились в тот момент, когда я делал kill -9 айскасту. Т.е. проблема явно была (и есть?) в libshout.

Сорцы: https://github.com/xiph/Icecast-libshout/blob/master/src/shout.c

Так в чем там хотябы теоретически может быть проблема?

deep-purple ★★★★★
()
Ответ на: комментарий от i-rinat

не помогает. я создаю сокет, прописываю ему параметры, далее в бесконечном цикле кручу poll потом recv если есть что читать и опять на poll.

подключаюсь, читаю и идет poll() хотя ssh давно погашен. пару минут примерно детектит

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

Кажется, я пропустил TCP_KEEPIDLE и TCP_KEEPCNT.

Попробуй для начала общесистемные умолчания поменять на меньшие значения и посмотреть, как изменится поведение. Меняются записью в файлы /proc/sys/net/ipv4/tcp_keepalive_intvl, /proc/sys/net/ipv4/tcp_keepalive_probes, /proc/sys/net/ipv4/tcp_keepalive_time.

Описания есть есть в man 7 tcp:

       tcp_keepalive_intvl (integer; default: 75; since Linux 2.4)
              The number of seconds between TCP keep-alive probes.

       tcp_keepalive_probes (integer; default: 9; since Linux 2.2)
              The maximum number of TCP keep-alive probes to send before  giv‐
              ing  up  and  killing  the connection if no response is obtained
              from the other end.

       tcp_keepalive_time (integer; default: 7200; since Linux 2.2)
              The number of seconds a connection needs to be idle  before  TCP
              begins sending out keep-alive probes.  Keep-alives are sent only
              when the SO_KEEPALIVE socket option  is  enabled.   The  default
              value  is  7200 seconds (2 hours).  An idle connection is termi‐
              nated after approximately an additional 11 minutes (9 probes  an
              interval of 75 seconds apart) when keep-alive is enabled.

              Note that underlying connection tracking mechanisms and applica‐
              tion timeouts may be much shorter.
i-rinat ★★★★★
()
Ответ на: комментарий от deep-purple

Так в чем там хотябы теоретически может быть проблема?

Не проще будет подцепиться с помощью gdb и посмотреть, где именно падает?

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat
# cat /proc/sys/net/ipv4/tcp_keepalive_time
1
# cat /proc/sys/net/ipv4/tcp_keepalive_intvl
1
# cat /proc/sys/net/ipv4/tcp_keepalive_probes
1

а детектит все так же через примерно 2 минуты хотя сейчас в коде вообще нет настройки сокета

quester ★★
() автор топика
Последнее исправление: quester (всего исправлений: 1)

$ man 2 poll

" POLLRDHUP (since Linux 2.6.17) Stream socket peer closed connection, or shut down writing half of connection. The _GNU_SOURCE feature test macro must be defined (before including any header files) in order to obtain this definition."

level1 ★★
()
Ответ на: комментарий от level1
netstat -tulpn
/etc/init.d/ssh stop
netstat -tulpn

netstat говорит что порт 22 слушали и перестали и только через 2 минуты я это вижу в виде события в программе

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

Если не посылает, то надо смотреть почему это он не посылает.

level1 ★★
()
Ответ на: комментарий от i-rinat

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

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

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

Скорее всего, не все ошибки обрабатываются.

i-rinat ★★★★★
()
Ответ на: комментарий от quester

Я попробовал ставить параметры на сокет, и у меня вполне себе сработало.

#include <arpa/inet.h>
#include <assert.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

int main(void) {
  int s = socket(AF_INET, SOCK_STREAM, 0);
  printf("s = %d\n", s);
  assert(s != -1);

  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr("192.168.111.1");
  addr.sin_port = htons(80);

  int r;
  int ka = 1;
  r = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka));
  assert(r == 0);

  int val = 1;
  r = setsockopt(s, SOL_TCP, TCP_KEEPINTVL, &val, sizeof(val));
  assert(r == 0);

  val = 1;
  r = setsockopt(s, SOL_TCP, TCP_KEEPIDLE, &val, sizeof(val));
  assert(r == 0);

  val = 1;
  r = setsockopt(s, SOL_TCP, TCP_KEEPCNT, &val, sizeof(val));
  assert(r == 0);

  r = connect(s, (struct sockaddr *)&addr, sizeof(addr));
  assert(r == 0);

  struct pollfd pfd = {.fd = s, .events = POLLIN | POLLERR | POLLHUP};

  while (1) {
    printf("calling poll()\n");
    r = poll(&pfd, 1, 999999);
    printf("   r = %d\n", r);
    if (r == 1) {
      printf("  pfd.revents = 0x%04x  ", pfd.revents);
      if (pfd.revents | POLLIN)
        printf(" POLLIN");
      if (pfd.revents | POLLOUT)
        printf(" POLLOUT");
      if (pfd.revents | POLLERR)
        printf(" POLLERR");
      if (pfd.revents | POLLHUP)
        printf(" POLLHUP");
      printf("\n");
    }
    sleep(1);
  }
}

Подсоединяюсь к веб-серверу на 192.168.111.1, вижу в Wireshark пакеты keepalive. Затем режу ответы от 192.168.111.1 с помощью iptables, вижу, как секунду выскакивает POLLHUP. Это ожидаемое поведение.

Просто параметры в /proc/sys недостаточно установить, SO_KEEPALIVE должно ставить само приложение.

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

протестировал. у тебя в if ошибка нужно & вместо |.

когда я натравил твою программу на nginx и опустил его, то да срабатывает сразу, присылая pfd.revents = 0x0001 POLLIN

если же использовать ssh: 127.0.0.1 22, то приходит только через две минуты: pfd.revents = 0x0019 POLLIN POLLERR POLLHUP

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

у тебя в if ошибка нужно & вместо |.

Хм. Забавно.

если же использовать ssh: 127.0.0.1 22, то приходит только через две минуты

А это странно. Не должно же быть разницы.

i-rinat ★★★★★
()
Ответ на: комментарий от quester

Да, вижу POLLHUP и при подключении к 22 порту, на котором ssh. Машина удалённая, пакеты в некоторый момент просто дропаю полностью через iptables.

i-rinat ★★★★★
()
Ответ на: комментарий от quester

программа методично пишет:

$ ./a.out 
s = 3
calling poll()
   r = 0
calling poll()
   r = 0
calling poll()
   r = 0
calling poll()
   r = 0
calling poll()
   r = 0
calling poll()

потом я делаю /etc/init.d/ssh stop, она еще две минуты то-же самое пишет и потом 0x0019 POLLIN POLLERR POLLHUP

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

потом я делаю /etc/init.d/ssh stop, она еще две минуты то-же самое пишет и потом 0x0019 POLLIN POLLERR POLLHUP

Так у тебя sshd продолжает работать: существующие сессии продолжают работать после того, как ты «останавливаешь» sshd.

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

хм. но порт ssh демон уже не случает что подтверждает netstat -tulpn

если ты прав то тогда почему оно отваливается через две минуты?

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

хм. но порт ssh демон уже не случает что подтверждает netstat -tulpn

Не вижу связи.

если ты прав то тогда почему оно отваливается через две минуты?

Звучит примерно как: «если ты прав, то тогда почему трава зелёная?»

Для начала нужно проверить, сколько времени живёт соединение с sshd сервером, если sshd не тушить. Думаю, будут все те же две минуты. Кстати, соединение завершает сам sshd, путём посылки FIN/ACK.

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

Для начала нужно проверить, сколько времени живёт соединение с sshd сервером, если sshd не тушить.

живет даже после выключения sshd, но не две минуты, не дождался. возможно там идет фоновый не видимый ping/pong

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

Я надеялся, что будет лучше, если ты сам посмотришь в руководство по openssh или в исходник. Но, видать, не судьба.

В общем, в openssh есть таймаут на аутентификацию. Если ты подключаешься к серверу, но не проходишь процедуру аутентификации, через некоторое время openssh закрывает соединение. Таймаут конфигурируется, но по умолчанию он равен 120 секундам. Ты явно просто подключаешься к серверу и ничего не шлёшь и не читаешь. Поэтому как раз подпадаешь под этот случай. Поэтому у тебя соединение отваливается через две минуты — openssh сам закрывает его.

i-rinat ★★★★★
()
Ответ на: комментарий от quester

возможно там идет фоновый не видимый ping/pong

Не нужно гадать, если можно просто посмотреть, собрав и проанализировав трафик.

i-rinat ★★★★★
()

Ты на си пишешь, как я понял, вопрос: почему не использовать libuv / libev / libevent для всего этого? Там, вроде, грамотно обыграны эти моменты с таймаутами tcp.

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

что бы понять моменты достаточно разобрать существующее
libevent libuv это библиотеки уровня джуниора если не ниже

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