LINUX.ORG.RU

Спецам по сетевому программированию


0

0

Что-то мучает меня вопрос связанный с приемом входящего соединения:

1. Неблокирующий сокет.

2. Для определения события используется select, poll, epoll, kquery

3. Один accept если выставлено POLLIN, далее передача обработки в срэд.

4. Переход к пункту 2.

Вопрос такой, если пришло два подключения с интервалом стремящимся к нулю, первое понятно дело примется. А вот второе оставит состояние POLLIN при следующем опросе? Что-то не могу найти конкретного ответа на этот вопрос.

ЗЫ: И да это все немного медленно, так как питон )



Последнее исправление: Alesh (всего исправлений: 1)
Ответ на: комментарий от mv

> Вызывай accept'ы до EAGAIN.

mv, это предположение или приходилось сталкиваться?

Я собственно подозревал, что так надо бы делать. Но не получалось с имитировать ситуацию, что бы убедиться в необходимости. А огород городить если POLLIN все равно останется при следующем пуллинге не хочется

Alesh
() автор топика
Ответ на: комментарий от Alesh
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

int main()
{
	int ret, sock;
	long arg;
	struct sockaddr_in addr = {
		.sin_family = AF_INET,
		.sin_port = htons(5000),
		.sin_addr = 0x0100007f
	}, in;
	socklen_t len;

	assert((sock = socket(AF_INET, SOCK_STREAM, 0)) > 0);
	assert(bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0);
	assert(listen(sock, 10) == 0);
	assert((arg = fcntl(sock, F_GETFL, NULL)) >= 0);
	assert(fcntl(sock, F_SETFL, arg | O_NONBLOCK) >= 0);
	sleep(10);
	do {
		ret = accept(sock, (struct sockaddr *)&in, &len);
		printf("fd: %d\n", ret);
	} while(ret > 0);
}

Пока процесс спит, делаем два раза telnet 127.0.0.1 5000. Получаем:

$ ./srv 
fd: 4
fd: 5
fd: -1

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

> Пока процесс спит, делаем два раза telnet 127.0.0.1 5000. Получаем:

В твоем примере то все очевидно.

Меня же интересует как будет вести себя пуллинг. То есть задали в listen() размер очереди 10. пришло сразу два подключения. Пуллинг показал POLLIN. accept я так понимаю извлечет из очереди одно соединение. После этого (в очереди остается еще одно соединение) пуллинг покажет POLLIN? Или он до следующего третьего подключения будет тишина.

Если POLLIN все таки возвращается, то делать в цикле assept() лишне, да и что-то такой такой подход коряво вписывается в мою идею фикс. ;)

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

Alesh
() автор топика

Вопрос такой, если пришло два подключения с интервалом стремящимся к нулю, первое понятно дело примется. А вот второе оставит состояние POLLIN при следующем опросе? Что-то не могу найти конкретного ответа на этот вопрос.

struct pollfds fds[N];
int rc = poll(&fds, N, timeout);
if (rc <= 0) ...
for (int i = 0; i < N && rc; i++) {
    if (fds[i].revents & POLLIN) {
        inf fd = accept(fds[i].fd, 0, 0);
        ......
        --rc;
    }
}
bibi
()
Ответ на: комментарий от mv

" assert((sock = socket(AF_INET, SOCK_STREAM, 0)) > 0); assert(bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0); assert(listen(sock, 10) == 0); assert((arg = fcntl(sock, F_GETFL, NULL)) >= 0); assert(fcntl(sock, F_SETFL, arg | O_NONBLOCK) >= 0); "

круто. а потом делаем релиз :)

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

круто. а потом делаем релиз :)

Ни за что! Только -O0 и -g3.

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

Тогда я не понял, что же именно хочет топикастер :-?

Чтобы select/poll возвращался, если с предыдущего вызова не весь бэклог был выгребен.

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

Чтобы select/poll возвращался, если с предыдущего вызова не весь бэклог был выгребен.

А он и так вернется. Если обработаны+сброшены не все события.

bibi
()

> А вот второе оставит состояние POLLIN при следующем опросе?

да. poll() «level triggered». iow, каждый вызов poll() проверяет
состояние. конкретно для этого случая см tcp_poll()

if (sk->sk_state == TCP_LISTEN)
return inet_csk_listen_poll(sk);

в случае epoll() зависит от EPOLLET.

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

>> Это не то.

Тогда я не понял, что же именно хочет топикастер :-?

Ты прокручиваешь зачем то в цикле все fds из пула. Типа у тебя несколько серверных сокетов запущено и ждут подключений. Это совсем не то, что я имел виду. Видимо действительно хреново доношу свою мысль.

Сейчас освобожусь и попробую прикрутить к коду mv poll или epoll. И по той же схеме со слипом закинуть в него несколько соединений как бы сразу. По идее должно быть видно сбросится ли POLLIN после первого акцепта.

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

> да. poll() «level triggered» ... см tcp_poll()

в случае epoll() зависит от EPOLLET.


спасибо погляжу внимательнее.

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

Ты прокручиваешь зачем то в цикле все fds из пула. Типа у тебя несколько серверных сокетов запущено и ждут подключений.

Ну не обязательно именно подключения - любого события. POLLIN лишь как пример. Зачем прокручивается цикл? IMHO это должно быть очевидно - могут сработать одновременно несколько событий. На одном сокете приходит соединение, на другом приняты данные на чтение а третий вообще дескриптор последовательного порта готовый на запись. После разблокировки select/poll, очевидно, обрабатывать нужно все события. Потому что при попытке запихнуть эти дескрипторы в select/poll повторно с тем же набором ожидаемых событий последний тут же разблокируется (события активны) и всё пойдёт по новой.

Это совсем не то, что я имел виду. Видимо действительно хреново доношу свою мысль.

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

ИТОГО

Alesh> Сейчас освобожусь и попробую прикрутить к коду mv poll или epoll.

Прикрутил, в общем не обязательно acceptом выгребать все соединения. Во всяком случае poll не сбрасывает POLLIN пока все соединения не были акцептованы.

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <poll.h>

int main()
{
   //int cnt = 0;
   int ret, sock;
   long arg;
   struct sockaddr_in addr = {
      .sin_family = AF_INET,
      .sin_port = htons(5000),
      .sin_addr = 0x0100007f
   }, in;
   socklen_t len;

   struct pollfd fds[1];


   assert((sock = socket(AF_INET, SOCK_STREAM, 0)) > 0);
   assert(bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0);
   assert(listen(sock, 10) == 0);
   assert((arg = fcntl(sock, F_GETFL, NULL)) >= 0);
   assert(fcntl(sock, F_SETFL, arg | O_NONBLOCK) >= 0);
   sleep(10);
   do {
      fds[0].fd = sock;
      fds[0].events = POLLIN;
      if((poll(fds, 1, 0)>0)&&(fds[0].revents & POLLIN))
      {
          printf("POLLINT detected!) on %d\n", fds[0].fd);
      }
      ret = accept(sock, (struct sockaddr *)&in, &len);
      printf("fd: %d\n", ret);
   } while(ret > 0);

  shutdown(sock, SHUT_RDWR);
  close(sock);
  getchar();
}

Результат:

POLLINT detected!) on 3
fd: 4
POLLINT detected!) on 3
fd: 5
fd: -1

Надеюсь что epoll и select ведут себя аналогично ;)

ЗЫ: Я не знаю то ли я чего-то ни то делаю, то ли GCC надо мной прикалывается ))) Но в дебажной версии если раскомментировать первую строчку в main результат выдается такой:

POLLINT detected!) on 3
fd: -1
Проверьте кто-нибудь на своем компе, а то я уже начинаю верить в зеленых человечков )))

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

А это ничего, что твой код в релизе не заработает?

Не переживай, я такой код не пишу :)

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

> А это ничего, что твой код в релизе не заработает

У меня наоборот в дебаге чудеса творились. см выше ) Кстати я до сих пор не пойму почему ))) Хотя подозреваю, что манера mv инициализировать структуры вынесла мозг gccу )))

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