2012-01-29 2 views
1

Я пытаюсь реализовать TCP-сервер, который является частью более крупного проекта. В основном сервер должен иметь возможность поддерживать TCP-соединение с любым количеством клиентов (минимум 32) и обслуживать любого клиента, который запрашивает обслуживание. В нашем сценарии дело в том, что предполагается, что, как только клиент будет подключен к серверу, он никогда не закроет соединение, если не произойдет какой-либо сбой (например, машина, на которой работает клиент, разрывается), и она будет повторно запрашивать услугу из сервер. То же самое происходит со всеми другими клиентами i-e, каждый из которых будет поддерживать соединение с сервером и выполнять транзакции. поэтому для подведения итогов сервер будет в то же время поддерживать соединение с клиентами, одновременно обслуживая каждый клиент по мере необходимости, а также должен иметь возможность принимать любые другие клиентские соединения, которые хотят подключиться к серверу.Multi Threaded Server design

Теперь я реализовал вышеуказанные функции, используя системный вызов API-интерфейсов berkely, и работает отлично, когда у нас небольшое количество клиентов (скажем, 10). Но сервер должен быть масштабирован до максимально возможного уровня, поскольку мы его реализуем на 16-ядерном компьютере. Для этого я просмотрел различные методы многопоточной обработки e-g одним потоком для каждого клиента и т. Д., И лучший из них, по моему мнению, будет проектом пула потоков. Теперь, когда я собирался реализовать это, я столкнулся с некоторыми проблемами: Если я определяю основной поток, чтобы принимать любое количество входящих соединений и сохранять каждое соединение Файловый дескриптор в структуре данных, и у меня есть пул потоков, как бы я мог получить потоки, чтобы опросить, является ли конкретный клиент запрашивающим обслуживание или нет. Дизайн достаточно прост для сценариев, в которых клиент связывается с сервером, и после получения услуги он закрывает соединение, чтобы мы могли выбрать поток из пула, обслуживать клиента, а затем вставлять его обратно в пул для последующей обработки соединения. Но когда мы должны обслуживать набор клиентов, которые поддерживают соединение и запрашивают услуги с перерывами, какой будет лучший подход для этого. Вся помощь будет очень признательна, поскольку я действительно застрял в этом. Спасибо.

+0

Вставьте свой код, давайте это прочитаем. Что не так с Apache? – YumYumYum

+0

Мне нужно разработать сервер на системном независимом языке C с использованием SOCKET API. – Abdullah

ответ

1

Используйте pthreads, с одним потоком на процессор и одной дополнительной нитью.

Дополнительная нить (основной поток) прослушивает новые соединения с системным вызовом listen(), принимает новые соединения с accept(), затем определяет, какой рабочий поток в настоящее время имеет наименьшее количество соединений, получает блокировку/mutex для очереди FIFO этого рабочего потока «ожидающих соединений» помещает дескриптор для принятого соединения в очередь FIFO рабочего очереди «ожидающих соединений» и отправляет уведомление «проверить вашу очередь» (например, используя трубу) в рабочий поток.

Рабочие потоки используют «select()» и отправляют/принимают данные в любые соединения, которые они приняли. Если/когда рабочий поток получает уведомление «проверить вашу очередь» из основного потока, он получит блокировку/мьютекс для своей очереди ожидающих соединений «FIFO» и добавит все вновь принятые соединения в свой список «fd_set».

Для 1024 подключений и 16 процессоров; вы можете столкнуться с одним основным потоком, ожидающим новых соединений (но почти ничего, поскольку вы не ожидали бы много новых подключений) и 16 рабочих потоков, обрабатывающих в среднем по 64 подключения каждый.

+0

Этот материал, связанный с подключением, слишком сложный и не имеет преимущества, поскольку простое простаивание потоков напрямую вызывает 'accept'. –

+0

@R: Я предпочел бы, чтобы потоки блокировались в ожидании «select()», чем потоки, которые постоянно тратили время на опрос центрального процессора как «accept()», так и «select()», или потоки, на которые требуется возраст, чтобы отвечать «accept () ", потому что они ждут" select() "для тайм-аута. Поток, который заблокирован в ожидании «select()», может получить запрос на соединение из основного потока, который был отправлен через. труба; что означает, что ваши рабочие потоки никогда не разблокируются, если у них нет реальной работы (без опроса). – Brendan

+0

Thnks alot @ Brendan. Я бы постарался это точно, это имеет смысл. – Abdullah

0

Один поток на клиента - это почти наверняка лучший дизайн. Убедитесь, что у вас всегда есть хотя бы один поток, заблокированный в accept, ожидающий нового подключения - это означает, что после принятия успешно вам может понадобиться создать новый поток, прежде чем продолжить, если он был последним. Я нашел семафоры отличным примитивным для отслеживания необходимости создавать новые потоки прослушивания.

+0

Проблема с «одним потоком на клиента» заключается в том, что при большой нагрузке все эти потоки борются за ограниченное количество процессоров, что приводит к увеличению задержки. Если вам нужно ответить на 1000 полученных пакетов, лучше иметь 500 обработанных пакетов и не запускать 500, чем иметь 1000 пакетов с половиной обработанными (и это становится хуже, если учесть стоимость переключателей потоков, борьба с потоками для блокировок и других задач масштабируемости). – Brendan

+0

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