Есть несколько легкодоступных программных библиотек, которые обеспечат эту функциональность.Основным, о котором я могу думать, является завиток.Вы можете найти простое введение в библиотеку curl multi здесь .
Обычно лучше не изобретать велосипед, если у вас нет веских причин (таких как улучшениемир технологий, или для академических исследований).
Ради академических исследований, и поскольку ответов "только для ссылок" будет недостаточно, я остановлюсь на одном из многих возможных способов, которыми можно было быперейдите к мультиплексированию сокетов.
Неблокирующие сокеты
Первый, и в настоящее время наиболее переносимый метод - это использовать неблокирующие сокеты и / или неблокируемыеблокирование вызовов сокетов, однако важно понимать (особенно при использовании неблокирующего сокета вызовы в отличие от установки O_NONBLOCK
для дескриптора файла): некоторые вещи все равно будут блокировать .Например, вы не можете получить connect
для немедленного возврата, если не установите файловый дескриптор в неблокирующий режим, и вы, конечно, getaddrinfo
(и аналогичные стандартные функции разрешения имен) тоже заблокируют.
Когда вы используете неблокирующие файлы или вызовы функций, функции возвращаются немедленно.Если данные не готовы, они укажут это через свои возвращаемые значения.Если есть данные, готовые для обработки, они снова будут показываться через возвращаемое значение.
Существует два способа (насколько мне известно) для обеспечения неблокирующих вызовов сокетов (включая connect
).
- Для Unix-подобных систем звоните
fcntl(socket_fd, fcntl(socket_fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK)
.После этого все вызовы на номера read
, write
, accept
и connect
будут возвращены немедленно, без задержки.connect
имеет несколько кодов ошибок (в errno
) специально для этого, таких как EALREADY
, EINPROGRESS
, EISCONN
и EWOULDBLOCK
, которые вы хотите проверить, потому что при включенииблокировка, некоторые возвращаемые значения ошибок на самом деле являются скрытыми возвращаемыми значениями успеха ;вам нужно проверить errno
. Для систем Windows, позвоните ioctlsocket(socket_fd, FIONBIO, (u_long[]){1})
.Будет происходить та же семантика, что и описанная выше, за исключением того, что коды errno
не будут кодами errno
(вместо них они будут GetLastError()
кодами), и они ... вероятно, разные значения, я не знаюя знаюОднако многие из них имеют похожие названия, поэтому в своих проектах я обычно использую что-то вроде:
#ifdef _WIN32
#define set_nonblock(fd) (ioctlsocket(fd, FIONBIO, (u_long[]){1}) == 0)
#define EAGAIN WSAEWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EISCONN WSAEISCONN
#define EINPROGRESS WSAEINPROGRESS
#define EINVAL WSAEINVAL
#define EALREADY WSAEALREADY
#else
#define set_nonblock(fd) (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) != -1)
#endif
Не заслуживает упоминания
Используя одни неблокирующие сокеты, я совсем не удивлюсь, если вам удастся поддерживать несколько тысяч подключений с помощью одного потока в различных системах с незначительными изменениями.Тем не менее, эта модель не идеальна, так как вам нужен занятый цикл, чтобы циклически проходить через каждый сокет, мгновенно проверяя их на наличие событий каждого цикла;вместо того, чтобы ваш код запускался ОС, например, при получении события.
Мы знаем, что для отправки событий в приложение ядру необходимо обрабатывать события, поэтому мы можем датьнапример, sleep(0);
как быстрое решение проблемы.Это приведет к падению использования процессора с почти 100% до менее 10% наверняка.Однако существует другой метод мультиплексирования многочисленных (блокирующих или нет) сокетов с неблокирующей (или прерванной по времени) функцией, так что функция будет немедленно возвращаться, когда будут доступны некоторые данные, или будет ждатьпока не истечет время для получения данных.
select
имеет явно существенные преимущества, однако есть и недостатки;а именно, наборы обычно ограничены небольшим количеством сокетов;для поддержки большого количества сокетов вам понадобится цикл внутри цикла, так как вы обнаружите, что ограничение 64 сокетов (или что бы то ни было) быстро истекает.Кроме того, не решает проблему блокировки connect
(где, как это делают методы O_NONBLOCK
и ~ FIONBIO`).
Таким образом, я больше не буду говорить о select
;Я опишу другие опции, доступные вам.Другой пример с аналогичными ограничениями - poll
;Я не буду говорить об этом, либо.Если вы хотите узнать об этом, в Интернете есть много об этом ...
Обратите внимание, что все с этого момента довольно непереносимо (хотя вы можете найти способы обернуть их все вобщий интерфейс, как curl multi ).
Асинхронные вызовы сокетов установят соединение, а затем сразу же вернутся, как неблокирующие вызовы сокетов,кроме того, они также подадут сигнал или вызовут функцию, которую вы укажете, когда соединение будет установлено.Это позволяет операционной системе уведомлять ваш код о наступлении событий, а не ОС, ожидающую вас.Должно быть понятно, что асинхронные сокеты идеально подходят для оптимизации, но они не переносимы .Существуют различные варианты для каждой ОС:
Все они имеют что-то общее, то есть то, что они вызывают функцию (илиподайте сигнал, который вы могли бы перевести на вызов функции) в случае успеха или неудачи.Тем не менее, их интерфейсы не так близки к тому, чтобы быть знакомыми.
Как правило, я не удосужился написать какую-либо оболочку для них, так как я нашел неблокирующие сокеты, которые я упомянул в начале этого ответаболее чем адекватны в наши дни.Важно то, что мне не нужно переносить его на каждую систему, потому что ... я слишком ленив для этого!Я оптимизирую систему только тогда, когда кто-то показывает мне, что она медленно работает в этой системе.В противном случае мы в конечном итоге копаемся в потоке систем, которые люди могут даже не использовать в наших программах ...