Comments 40
В чем смысл делать сокеты неблокирующими — а потом вызывать методы в цикле?
0
В чем смысл делать сокеты неблокирующими — а потом вызывать методы в цикле?
Это простой пример, демонстрирующий принцип работы.
В реальных задачах, в каждом цикле вызывается callback функция в которой процессу можно заниматься какимито другими задачами.
В случае блокирующих сокетов процесс тупо виснет на каждой блокирующей функции и ничего с ним сделать невозможно.
-4
В реальных задачах используется хотя бы select, не говоря уже о более продвинутых способах.
Активное ожидание, пусть и с вызовом sleep — суть костыль.
Активное ожидание, пусть и с вызовом sleep — суть костыль.
+4
В реальных задачах используется хотя бы select
Приведите аргумент в пользу select и
более продвинутых способовдля слушающего сокета?
Для «рабочих» клиентских сокетов смысл есть, согласен. Только в приведенном примере этого смысла нет, потому как тут обрабатывается одно единственное подключение.
-4
Есди сервер обрабатывает одно-единственное подключение, то зачем вообще неблокирующие сокеты?
0
Это туториал!
Если перестанете кидать минусы — напишу, как из этого примера сделать реально работающий сервер для любого количества подключений ))
Если перестанете кидать минусы — напишу, как из этого примера сделать реально работающий сервер для любого количества подключений ))
-2
Даже если закидают, пост всё равно нужен, чтобы его закидали плюсами.
PS: На самом деле магии в использовании select,epoll,kqueue довольно много, простые примеры они простые, но если речь идёт о работе на разных платформах под большими нагрузками, то начинают всплывать различны мелкие проблемки.
Самый простой вариант использовать готовые билиотеки для обработки событий, просто использовать и работают стабильно.
Можно использовать код из моей Астры: bitbucket.org/cesbo/astra/src/astra-4/core/event.c (поддерживает select,poll,epoll,queue) гоняет гигабиты ТВ.
PS: На самом деле магии в использовании select,epoll,kqueue довольно много, простые примеры они простые, но если речь идёт о работе на разных платформах под большими нагрузками, то начинают всплывать различны мелкие проблемки.
Самый простой вариант использовать готовые билиотеки для обработки событий, просто использовать и работают стабильно.
Можно использовать код из моей Астры: bitbucket.org/cesbo/astra/src/astra-4/core/event.c (поддерживает select,poll,epoll,queue) гоняет гигабиты ТВ.
0
Пишите. Потому что этот туториал ничему не учит…
+4
Я бы посоветовал вам смотреть на что-нибудь вроде www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/examples.html#boost_asio.examples.ssl.
Это и кроссплатформенно, и на C++, и не подвержено проблеме c10k и вообще самый правильный способ.
Это и кроссплатформенно, и на C++, и не подвержено проблеме c10k и вообще самый правильный способ.
+1
Спасибо за совет. Я знаю про boost::asio.
Из вашего поста следует, что мой код подвержен
Было бы любопытно почитать ваши доводы на эту тему.
Из вашего поста следует, что мой код подвержен
проблеме c10kи является «неправильным способом», я правильно понимаю?
Было бы любопытно почитать ваши доводы на эту тему.
-2
Сколько соединений сможет одновременно «держать» ваш сервер? Я не вижу полного кода, поэтому тут либо 1, либо 1000. Это и есть проблема.
Обычный многопоточный сервер, работающий на блокирующих сокетах, держит 1000 соединений. Потом заканчиваются ресурсы операционной системы — и она не дает создавать больше потоков. Или все начинает тормозить и лагать.
Неблокирующие сокеты — это способ повысить лимит числа соединений дальше. Классическая схема с select поддерживает до 10 000 соединений, дальше начинаются проблемы в силу квадратичной природы алгоритма. Однако, и большее число соединений держится, пусть и с лагами.
Дальше идут такие вещи, как poll, epoll, /dev/poll, kevent, асинхронные сокеты и куча других страшных слов, с которыми лично я никогда не работал. Там можно обрабатывать одновременно столько соединений, сколько способен держать сетевой стек ОС — своих накладных расходов эти способы накладывают крайне мало.
Обычный многопоточный сервер, работающий на блокирующих сокетах, держит 1000 соединений. Потом заканчиваются ресурсы операционной системы — и она не дает создавать больше потоков. Или все начинает тормозить и лагать.
Неблокирующие сокеты — это способ повысить лимит числа соединений дальше. Классическая схема с select поддерживает до 10 000 соединений, дальше начинаются проблемы в силу квадратичной природы алгоритма. Однако, и большее число соединений держится, пусть и с лагами.
Дальше идут такие вещи, как poll, epoll, /dev/poll, kevent, асинхронные сокеты и куча других страшных слов, с которыми лично я никогда не работал. Там можно обрабатывать одновременно столько соединений, сколько способен держать сетевой стек ОС — своих накладных расходов эти способы накладывают крайне мало.
+3
Сколько соединений сможет одновременно «держать» ваш сервер?
Хоть сколько.
Сервер в моей статье не использует ни select ни epoll ничего!
Да, это не правильно, но так тоже можно.
В принципе, если меня не закидают минусами, я могу показать, как пишется кроссплатформенный сервер с select и epoll. Он будет поддерживать тоже любое количество соединений в Linux, а в Windows — сколько позволит select.
Ничего там сверхъестественного нет.
-4
Хоть сколько.Неверно. Тот код, который я вижу, вообще не сможет обработать более одного соединения — это еще хуже, чем многопоточный сервер.
Но даже если в ваш сервер добавить многопоточность — то он все равно будет хуже, чем такой же сервер на блокирующих сокетах.
+3
Дальше идут такие вещи, как poll, epoll, /dev/poll, kevent, асинхронные сокеты и куча других страшных слов, с которыми лично я никогда не работал.
Но даже если в ваш сервер добавить многопоточность — то он все равно будет хуже, чем такой же сервер на блокирующих сокетах.
Ну вам то оно конечно виднее, но по моему вы меня тролите.
Но даже если в ваш сервер добавить многопоточность — то он все равно будет хуже, чем такой же сервер на блокирующих сокетах.
Ну вам то оно конечно виднее, но по моему вы меня тролите.
-4
Может кто опровергнет или подтвердит. Помоему при установке соединения (handshake) openssl блокирует всё и вся прелесть неблокирующих сокетов теряется.
0
Опровергаю: nginx использует openssl — но в использовании блокирующих сокетов никогда замечен не был.
+4
Тут и я не нужен, достаточно на код автора посмотреть, он хоть очень странный, написан просто ужасно, да и выедает процессор в бесконечном цикле так, что лучше бы уж блокировался, но тем не менее, из него видно, что SSL_do_handshake/SSL_read/SSL_write возвращают управление в функцию, сигнализируя SSL_ERROR_WANT_READ и SSL_ERROR_WANT_WRITE, после чего остается только взвести таймер и положить дескриптор на соответствующее событие в epoll/kqueue/eventports/по_вкусу.
If the underlying BIO is non-blocking, SSL_do_handshake() will also return when the underlying BIO could not satisfy the needs of SSL_do_handshake() to continue the handshake. In this case a call to SSL_get_error() with the return value of SSL_do_handshake() will yield SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. The calling process then must repeat the call after taking appropriate action to satisfy the needs of SSL_do_handshake(). The action depends on the underlying BIO. When using a non-blocking socket, nothing is to be done, but select() can be used to check for the required condition. When using a buffering BIO, like a BIO pair, data must be written into or retrieved out of the BIO before being able to continue.www.openssl.org/docs/ssl/SSL_do_handshake.html
+1
Поправочка. Автор и accept'ит соединение с помощью OpenSSL, поэтому не пользуется SSL_do_handshake(), но сути дела это не меняет, она работает также: www.openssl.org/docs/ssl/SSL_accept.html
+1
Вместо дескриптора сокета accept теперь вернет значение (-1).
Вы же кроссплатформенный код пишите, нельзя так делать. accept возвращает -1 в никсах, а в винде она возращает INVALID_SOCKET который ещё надо проверить и убедиться что там WSAEWOULDBLOCK, а не ошибка.
И немного не понял смысл замены блокирующего вызова на зацикленный неблокирующий.
Неблокирующий сокет нужен для обработки циклом клиентов и приёма новых конектов в одной ните, вместо разнесения этих операций по разным нитям.
+3
accept возвращает -1 в никсах, а в винде она возращает INVALID_SOCKET
INVALID_SOCKET в винде равен -1.
Хотя согласен, можно добавить этот макрос для линукса. Про WSAEWOULDBLOCK — не вижу смысла его проверять для сервера: ну ошибка и ошибка, все равно будем в цикле слушать.
И немного не понял смысл замены блокирующего вызова на зацикленный неблокирующий.
Вы же сами ответили:
Неблокирующий сокет нужен для обработки циклом клиентов и приёма новых конектов в одной ните, вместо разнесения этих операций по разным нитям.
-2
Тип SOCKET, в винде беззнаковый, а в никсах сокеты имеют знаковый тип int, а у Вас все присваивается типу int.
+3
Уже ответили что в винде не -1. Товарищи, разрабатывающие pvs studio пишут про эту ошибку чуть ли не в каждой статье.
К тому же в MSDN вполне чёрным по белом что ошибка функции accept это именно INVALID_SOCKET, а не что-то другое. И именно на него надо проверять если нет желания когда-то потом мучаться с совместимостью и переносимостью кода.
Стоило хотя бы упомянуть что код ошибки стоит проверять что это действительно всё ок для неблокирующего вызова. Это всего пара лишних макросов чтобы свести к единому виду, можно даже было бы привести в статье.
Я в примерах вижу в цикле вызывает accept без каких либо других действий внутри цикла. А обработка коннекта идёт после выхода из него.
Чем это отличается блокирующего вызова кроме ухудшения производительности из-за слипа?
Должно быть примерно так всё же:
К тому же в MSDN вполне чёрным по белом что ошибка функции accept это именно INVALID_SOCKET, а не что-то другое. И именно на него надо проверять если нет желания когда-то потом мучаться с совместимостью и переносимостью кода.
Стоило хотя бы упомянуть что код ошибки стоит проверять что это действительно всё ок для неблокирующего вызова. Это всего пара лишних макросов чтобы свести к единому виду, можно даже было бы привести в статье.
Вы же сами ответили:
Я в примерах вижу в цикле вызывает accept без каких либо других действий внутри цикла. А обработка коннекта идёт после выхода из него.
Чем это отличается блокирующего вызова кроме ухудшения производительности из-за слипа?
Должно быть примерно так всё же:
while(1) {
accept
...
// делаем что-то там нам нужное со старыми открытыми коннектами или с новым
}
...
// закрваем сокеты
+2
Да я вообще-то планировал сделать несколько статей, в каждой из которых постепенно добавлять куски кода, чтобы в конце концов выложить тут полноценный однопоточный кроссплатворменный сервер для высоконагруженных проектов.
Сомневаюсь, что кому-то была бы интересна взявшаяся из воздуха портянка кода из 100500 классов по 9000 строк. Любители таких развлечений ковыряют исходники nginx, а не на форумах сидят.
А вот сейчас, когда мне слили тут всю карму, как то не особенно и хочется уже продолжать.
ЗЫ. Не знаю как в винде, а в Visual Studio 2012 INVALID_SOCKET равен -1. Можете проверить, ссылка на готовый проект есть в статье.
Сомневаюсь, что кому-то была бы интересна взявшаяся из воздуха портянка кода из 100500 классов по 9000 строк. Любители таких развлечений ковыряют исходники nginx, а не на форумах сидят.
А вот сейчас, когда мне слили тут всю карму, как то не особенно и хочется уже продолжать.
ЗЫ. Не знаю как в винде, а в Visual Studio 2012 INVALID_SOCKET равен -1. Можете проверить, ссылка на готовый проект есть в статье.
-2
WinSock.h
Разницу между -1 и ~0 надеюсь объяснять не надо?
typedef UINT_PTR SOCKET;
#define INVALID_SOCKET (SOCKET)(~0)
Разницу между -1 и ~0 надеюсь объяснять не надо?
+1
Надо, объясните если не затруднит
0
Значение ~0 зависит от типа. Двоичное представление у него в виде всех единиц: для знакового это будет соответствовать -1, для беззнакового максимальное допустимое значение.
0
ОК, продолжите свою мысль:
в Windows код
int sd = accept (listen_sd, (struct sockaddr*) &sa_cli, (int *)&client_len);
в случае ошибки вернет какое значение для переменной sd?
Или что то же самое:
в Windows код
int sd = INVALID_SOCKET
вернет какое значение для переменной sd?
в Windows код
int sd = accept (listen_sd, (struct sockaddr*) &sa_cli, (int *)&client_len);
в случае ошибки вернет какое значение для переменной sd?
Или что то же самое:
в Windows код
int sd = INVALID_SOCKET
вернет какое значение для переменной sd?
-3
Вообще не вижу смысла о чём-то говорить если в винде пишите int sd а не SOCKET sd
За этим заканчиваю обсуждение.
За этим заканчиваю обсуждение.
+4
А что не так? Я в C/C++ новичок, но мне вот очевидно, что автор прав. В винде получится именно -1 при приведении к int именно из-за того, что в битовой форме все единицы.
0
Ну хотя бы то не так, что на винде дескриптор сокета - не число, а .указатель. И приведение его к int в лучшем случае просто не скомпилируется, а в худшем потеряет старшие биты (на 64х битных платформах) и не будет работать.
-1
Не знаю как в винде, а в Visual Studio 2012 INVALID_SOCKET равен -1. Можете проверить, ссылка на готовый проект есть в статье.
Нельзя использовать магические константы! Разработчки, ну зачем вы сами себе собираете атомную бомбу, с таймером произвольного запуска.
0
а где вообще можно глянуть код целиком?
0
а где вообще можно глянуть код целиком?
Дочитайте статью до конца — может найдете ответ на свой вопрос.
Дочитайте статью до конца — может найдете ответ на свой вопрос.
0
2014 год, select. Где-то тут делят на 0. Есть же iocp, epoll, libevent.
0
Теоретически, вместо бесконечного цикла, можно с помощью функции select и ее более мощных аналогов, ждать пока сокет listen_sd станет доступен для чтения, а лишь потом один раз вызвать accept. Но лично я не вижу в моем способе с циклом никаких особых недостатков.
Как-то тоже был интерес к написанию http-сервера в качестве тестового задания. А получился пост :) В нем как раз можно и более «мощные средства» найти. В них бывает польза… Правда приведенный материал без ssl.
0
Sign up to leave a comment.
Кроссплатформенный https сервер с неблокирующими сокетами