Используя несколько сокетов, лучше ли неблокирующее или блокирующее с помощью select?

StackOverflow https://stackoverflow.com/questions/1526253

Вопрос

Допустим, у меня есть серверная программа, которая может принимать подключения от 10 (или более) разных клиентов.Клиенты отправляют данные случайным образом, которые принимаются сервером, но можно быть уверенным, что по крайней мере один клиент будет отправлять данные при каждом обновлении.Сервер не может дождаться поступления информации, поскольку ему нужно выполнить другую обработку.Помимо использования асинхронных сокетов, я вижу два варианта:

  1. Сделайте все сокеты неблокирующими.В цикле вызовите recv() на каждом сокете и разрешить ему выйти из строя с WSAEWOULDBLOCK если доступных данных нет, и если мне посчастливится получить какие-то данные, то сохраните их.

  2. Оставьте розетки блокирующими.Добавьте все сокеты в FD_SET и позвонить select().Если возвращаемое значение ненулевое (каковым оно и будет большую часть времени), переберите все сокеты, чтобы найти соответствующее количество читаемых сокетов с FD_ISSET() и только позвони recv() на читаемых сокетах.

Первый вариант создаст намного больше обращений к recv() функция.Второй метод является более сложным с точки зрения программирования из-за всех FD_SET и FD_ISSET зацикливание.

Какой метод (или другой метод) предпочтительнее?Избегает накладных расходов на сдачу recv() сбой в неблокирующем сокете, стоящий хлопот с вызовом select()?

Я думаю, что понимаю оба метода, и я успешно пробовал оба, но я не знаю, считается ли один из способов лучшим или оптимальным.

Это было полезно?

Решение

Я бы рекомендовал использовать перекрытый ввод - вывод вместо этого.Затем вы можете запустить WSARecv(), и предоставить функцию обратного вызова, которая будет вызвана по завершении операции.Более того, поскольку он будет вызываться только тогда, когда ваша программа находится в состоянии ожидания с возможностью оповещения, вам не нужно беспокоиться о блокировках, как в многопоточном приложении (при условии, что вы запускаете их в своем основном потоке).

Обратите, однако, внимание, что вы делай необходимо часто входить в такое состояние ожидания с возможностью оповещения.Если это ваш поток пользовательского интерфейса, обязательно используйте MsgWaitForMultipleObjectsEx() в вашем цикле сообщений, с помощью MWMO_ALERTABLE Отметить.Это даст вашим обратным вызовам шанс на выполнение.В потоках, отличных от пользовательского интерфейса, регулярно вызывайте любой из функции ожидания это переводит вас в состояние ожидания, вызывающее тревогу.

Обратите также внимание, что модальные диалоги обычно не переходят в состояние ожидания с возможностью оповещения, поскольку у них есть свой собственный цикл сообщений, который не вызывает MsgWaitForMultipleObjectsEx().Если вам нужно обработать сетевой ввод-вывод при отображении диалогового окна, выполняйте весь сетевой ввод-вывод в выделенном потоке, который регулярно переходит в состояние ожидания с возможностью оповещения.

Если по какой-либо причине вы не можете использовать перекрытый ввод-вывод - обязательно используйте блокировку select().Использование неблокирующего recv() подобное в бесконечном цикле - непростительная трата процессорного времени.Однако переведите сокеты в неблокирующий режим - в противном случае, если поступит один байт и вы попытаетесь прочитать два, вы можете неожиданно заблокировать.

Возможно, вы также захотите рассмотреть возможность использования библиотеки, чтобы абстрагироваться от сложных деталей.Например, событие libevent или повышение ::asio.

Другие советы

ввод-вывод должен быть либо полностью заблокирован одним потоком на соединение, и в этом случае цикл событий по сути является планировщиком ОС, либо ввод-вывод должен быть полностью неблокирующим, и в этом случае цикл событий на основе select / waitformultipleobjects будет в вашем приложении

Все промежуточные варианты не очень удобны в обслуживании и подвержены ошибкам

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

Для полностью неблокирующего ввода-вывода ядром приложения является цикл событий на основе select / waitformultipleobjects, все сокеты находятся в неблокирующем режиме, все чтения / записи обычно выполняются из потока цикла событий (для максимальной производительности запись может быть сначала предпринята непосредственно из потока, запрашивающего запись)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top