каков наилучший способ обработки новых клиентов с помощью select() на сервере?

#c #sockets #select #asynchronous

#c #сокеты #выберите #асинхронный

Вопрос:

Я хочу написать асинхронный сервер сокетов на C, но прежде чем я это сделаю, я проведу некоторое исследование. Рассмотрим пример сокета select(), показанный здесь: http://www.gnu.org/s/hello/manual/libc/Server-Example.html#Server-Example Я вижу, что примерная программа будет принимать только одного клиента для каждого цикла выбора (если я правильно читаю). Итак, если есть 20 клиентов и еще два пытаются подключиться, примет ли он только 21-го клиента, затем обработает остальные 20 (в худшем случае, предполагая, что все остальные 20 требуют чтения), а ЗАТЕМ примет 22-й? Было бы лучше, если бы я прервал цикл после принятия клиента, чтобы он мог снова выбрать () и позаботиться обо всех ожидающих клиентах перед обработкой подключенных? Или это противоречит цели использования select()? Спасибо.

Ответ №1:

Шаблон сервера, показанный в примере, на который вы ссылались, в порядке; нет никаких существенных проблем, связанных с тем, что цикл принимает только один сокет на итерацию.

Ключевой момент, который следует иметь в виду, заключается в том, что в хорошо продуманном цикле select() единственное место, которое процесс должен блокировать, находится внутри вызова select() . В частности, при правильном кодировании сервер никогда не заблокирует внутри send() , recv() или accept() . Лучше всего установить все сокеты в неблокирующий режим (через fcntl(fd, F_SETFL, O_NONBLOCK)), чтобы гарантировать такое поведение.

Учитывая это, точный порядок «какие клиенты обслуживаются первыми в любой конкретной итерации цикла событий» не имеет значения, потому что все сокеты клиентов обрабатываются очень скоро после того, как у них есть данные, готовые к чтению (или буферное пространство, готовое к записи), и все новые соединения принимаются быстро.

Комментарии:

1. Конечно, вы могли бы ограничить объем данных, которые вы отправляете / получаете на каждой итерации, чтобы избежать длинных запросов, останавливающих более короткие. Я думаю, что веб-сервер Zeus сделал что-то в этом роде. Вероятно, вам следует также добавить некоторую случайность в отношении того, какие запросы вы обрабатываете в первую очередь.

2. Спасибо, я думал, что смогу исправить ошибку в своей программе, прервав работу после принятия клиента, но это фактически снизило бы производительность. Думаю, я не могу выбрать простой выход.

Ответ №2:

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

Ответ №3:

Вы можете использовать poll() в прослушивающем сокете после accept(), чтобы узнать, есть ли еще клиенты, ожидающие подключения.

Обратите внимание, что количество одновременных попыток подключения обрабатывается параметром backlog для прослушивания (server_sock, backlog) — backlog равен 1 в примере, на который вы ссылаетесь.