LINUX.ORG.RU

Слушать несколько портов.

 ,


0

1

Всем привет, есть потребность слушать одновременно несколько TCP портов (кол-во может быть от 20 до 100). Естественно на каждый порт будут стучаться клиенты, слать сообщений и т.п В качестве обработчика планирую использовать LibEvent, ранее делал с ним приложение, но слушал только один порт. Поэтому вопрос как лучше смоделировать приложение в моем случае? Сделать одно приложение, но создавать для каждого порта свой поток (pthread) или под каждый тсп порт создавать свое отдельное приложение?

Я бы сделал один обработчик событий от всех используемых портов и отдавал бы открытые сокеты распаралеленным обработчикам данных.

four_str_sam
()

создать 1 поток для сетевой работы + использовать мультиплексирование

vvviperrr ★★★★★
()

Libevent событийно-ориентированный. Ему пофиг сколько у тебя сокетов есть, тебе просто надо на каждый слушающий сокет зарегистрировать колбэк на on_connect и всё.

PS рекомендую libev.

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

Так если регать колбэк в основном потоке, то все соединение так и будет крутиться в этом же потоке. Имеет ли смысл выносить в разные потоки?

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

Имеет ли смысл выносить в разные потоки?

Это в первую очередь зависит от задачи. Если нет интенсивных вычислений и не стоит задача обрабатывать тыщи qps то это всё абсолютно не нужно. Если же у тебя длинные и блокируемые операции то это совсем другое.

Только ты определись чего ты хочешь: non-blocking io или же «по треду на соединения». Это взаимоисключающие вещи.

Возможно, ты хотел каждый слушающий порт обслуживать в своём потоке? Или создать пул воркеров? Всё возможно, гугл в помощь.

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

Задача в том чтобы принимать пакеты от различных устройств, у устройств разные протоколы поэтому для удобства планирую под каждый протокол выделить свой порт. Дальше устройство постучалось - сервер разобрал его пакет и отправил его другому приложению. Клиентов таких может быть много (до 10К).

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

Хм, я подумал и решил что проще каждый тип устройств обрабатывать в своём процессе — так будет надёжнее и можно будет независимо рестартовать процессы без сброса всех соединений. Это при условии что процессам не надо общаться друг с другом. Иначе всё может быть несколько сложнее.

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

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

Т.е под каждый порт делать отдельный сервис? Но так сложнее будет контролировать, если таких портов наберется больше 30 штук уже.

По идее этим процессам между собой общаться не надо. Изначально планировал сделать такую схему: один сервис в котором создаются треды для парсера пакетов с тсп порта (один тред - один порт). При получении пакета оно разбирается и складывается в некий буфер. В этом же приложении отдельный тред следит за содержанием буфера, и если в буфере есть данные то отправляет их через сокет стороннему сервису обработчику (он уже упаковывает данные в базу).

Ну и с таком системой по идее нагрузка на БД должна быть меньше. А если под каждый порт делать свой сервис, то и обработчик должен держать несколько соединений.

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

Объясни, в чем смысл использовать libev, если можно сразу напрямую делать select/poll/epoll с вызовом pthread_create?

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от hell_wood

А если сделать один сервис, но номер порта передавать из командной строки? И попробовать запускать его при поступлении коннекта при помощи xinetd?

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

Объясни, в чем смысл использовать libev, если можно сразу напрямую делать select/poll/epoll с вызовом pthread_create?

Удобство и портабельность. Не знаю как сейчас, а раньше epoll был лютым набором граблей и глюков. О типичных проблемах можно почитать тут: http://doc.dvgu.ru/devel/ev.html#the_special_problem_of_disappearing_file_des... (и всё что ниже).

pthread_create

Как я уже говорил, тут либо крестик, либо трусы. Суть async io (epoll, etc) в том чтобы не делать pthread_create на каждого клиента.

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

Черт! xinetd не умеет передавать запускаемому демону номер порта ☹

Тады только открывать N сокетов и поочередно поллингом заниматься. А можно и параллельно — по одному потоку на каждый bind запустить, а потом из них запускать потоки при коннекте.

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

ХЗ, я никогда epoll не использовал. Только старый добрый select:

int waittoread(int sock){
	fd_set fds;
	struct timeval timeout;
	int rc;
	timeout.tv_sec = 0; // wait not more than 30 millisecond
	timeout.tv_usec = 30000;
	FD_ZERO(&fds);
	FD_SET(sock, &fds);
	rc = select(sock+1, &fds, NULL, NULL, &timeout);
	if(rc < 0){
		perror("select failed");
		return 0;
	}
	if(rc > 0 && FD_ISSET(sock, &fds)) return 1;
	return 0;
}

Суть async io (epoll, etc) в том чтобы не делать pthread_create на каждого клиента.

До меня это не доходит. Мне проще поток запустить, чем пытаться разобраться, как в одном потоке 100500 клиентов обслужить...

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

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

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

Если делать все в отдельных сервисах, то получаем независимость друг от друга, но тогда «сложнее» будет передавать данные обработчику.

Если делать все в одном сервисе и выносить в разные треды. Тогда будет один общий буфер для всех данных и один открытый сокет для связи с обработчиком.

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

Без подробностей бесполезно обсуждать такие вещи предметно. Тебе нужно хотя бы qps прикинуть. А вот кол-во соединений практически ни на что не влияет. Про БД я не понял. 30 отдельных соединений это вполне разумная цифра для всех БД что я знаю.

Потом, если задача серьёзная то стоит подумать о резервировании сервера.

Короче, что-то мне подсказывает что тебе подойдёт любая архитектура. Реально, я не думаю что парсинг сообщений будет препятствием для обработки сотен запросов в секунду. Ну а выдержит ли БД смотри сам, проверить простым скриптом несложно.

следит за содержанием буфера

Тебе нужны очереди.

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

Мне проще поток запустить, чем пытаться разобраться, как в одном потоке 100500 клиентов обслужить...

Можешь и так, современное железо всё стерпит. Но зачем тебе select я так и не понял. Делаешь блокирующий read(sockfd, BUFZISE) и всё.

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

Но зачем тебе select я так и не понял

Это для клиента. В цикле какие-то действия выполняются и периодически проверяется наличие данных на входе. На сервере, кстати, тоже иной раз для упрощения (чтобы лишних IPC или глобальных ключей не городить) использую такой подход. Ну, от задачи зависит.

А вот в консольных приложениях у меня часто select используется: для опроса данных на входе + для опроса клавиатуры (не ввел ли пользователь текст) и т.п.

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

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

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

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

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

поможет ли вообще вынесение «прослушки» одного порта в отдельный тред снизить нагрузку или все это можно засунуть в один тред и не париться.

В смысле поможет ли вынесение listen в отдельный поток? Однозначно нет. Или ты имел в виду ожидание событий на сокете? Смотря что у тебя за архитектура, но осмелюсь сказать что это не основной bottleneck и поэтому пофиг.

Ты можешь обработку соединений каждого порта засунуть в свой тред. Готовые данные можешь складывать в очередь из которой отдельный воркер будет лить в базу. Тогда у тебя будет возможность использовать больше чем одно ядро и вроде как масштабируемое решение. Мне кажется это разумным компромисом с точки зрения производительности и масштабируемости и некостыльности.

Но даже если вы запустите по треду на каждое соединение(чего делать не надо, но если очень хочется...) то сервер всё равно выдержит эти несчастные 10к соединений (если только он не 32-бит).

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

Ну что-то подобное я имел ввиду: В каждом новом треде:

struct evconnlistener *ev_listen;
struct event_base *ev_base;

ev_listen = evconnlistener_new_bind(ev_base, on_accept, NULL,
											(LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE),
											-1, (struct sockaddr *)&server, sizeof(server));

Как-то так. Ну соответственно в каждом треде будет своя переменная ev_listen и ev_base. И в каждом треде запускать event_base_dispatch(ev_base);

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

Да, хороший вариант. Хотя, мне кажется, как тут выше написали, libuv/nodejs будут проще и удобнее. Но на их освоение уйдёт время.

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