Как стать автором
Обновить

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

Что то накрылся ваш сервер, хотя всего 256 просмотров страницы…
Да ладно, все работает :)

Вам повезло, у меня до сих пор не работает (
Интересно, какие параметры железа сервера. У меня сайты на дешевеньком vds хабраэффект выдерживали (wsgi+python)
Вообще я хотел указать на кодировку :)
Вам он хоть что то отдал, а мне ничего ( Действительно, «Самый короткий веб-сервер на с++» ))
Да, я прошу прощения за баги потому что не было возможности протестировать под нагрузкой, а о логах как-то не позаботился.
Специально для такого случая есть утилита ab, чтобы понять, как приложение будет себя вести под нагрузкой.
С английским у вас не очень, к сожалению.
3s3s починил свой английский в исходниках, и ничего не сказал ;)
Мне хабр все больше нравится: я тут всего второй месяц, а уже и про опцию -Wall успел узнать и английскому подучиться )))
А как же -Wextra? Она еще подробнее на баги указывает :)
Да, про нее мне тоже кто-то говорил. Надо будет попробовать.
-Werror не забудьте ;)
-Weffc++ — крутая опция
Есть ещё одна киллер-опция — -O4.
Не, вот эту как раз не надо, начинаются генерироваться warning'и в тех местах, где не нужно. Мой набор:
-pedantic -Wall -Wextra -Wunused-parameter -Wstrict-overflow=5 -Wundef -Wshadow -Wcast-qual -Wcast-align -Wconversion -Wsign-conversion -Wmissing-declarations -Wredundant-decls -Wdisabled-optimization -Woverloaded-virtual -Wctor-dtor-privacy -Wold-style-cast
Только для gcc (clang его не понимает), добавляется ещё -Wlogical-op
И один отключающий, т.к. не работает нормально с boost::optional:
-Wno-maybe-uninitialized
#define лучше более логичным static const string заменить.
Страуструп вообще не рекомендует использовать дефайны там, где можно обойтись без них. И сожалеет, что сама STL ими полнится.
Префикс — это хорошо. Когда он к месту. Представьте, что посмотрели на код спустя 20 лет, забывши о нём напрочь. Что подумаете, глядя на эти префиксы, которые никак не откомментированы? ужаснётесь…
Понятно, да, что например s — states, но. Нужно тратить время, чтобы до этого додуматься. А код должен быть понятным сразу.
И ещё… комментарий, дублирующий код — далеко не всегда хороший комментарий. Масло масленое.
Лучше, если комментарий отвечает на вопрос «для чего этот код?» — именно так программисту, смотрящему на код впервые, проще всего в нём разобраться. А если вот так вдруг посмотришь сначала на пояснение. а потом на код, который просто повторяет пояснение — по факту от этого ничего не получаешь, кроме представления о том, что «этот код работает так, как описано. Или — должен так работать».
P.S. Не понравилось — промолчал бы. Так критикую только то, что понравилось, ибо развития хочу. Очень хорошо, что тут не приходится упоминать про методологию ПКБ… ибо код — есть. Дисциплины бы этому качеству прибавить — и цены не будет вашему коду. Всего доброго!
#define лучше более логичным static const string заменить.
Страуструп вообще не рекомендует использовать дефайны там, где можно обойтись без них


Там где можно — да, но ведь строка из define намертво ляжет в код приложения, а static const string является объектом и будет создан из кода во время старта приложения, т.е. строка будет дублировано размещена в страницах данных, разве не так?

p.s: ну и глобальные объекты это довольно коварное зло, но в данном случае это уже не важно.
#define только лишь говорит препроцессору: «Замени вот это на то», по сути позволяя создавать что-то вроде своего внутреннего языка. А написав static const string можно гарантировать: вот — единственная строка с единственным же неизменяемым значением, которую — в случае необходимости — можно будет завернуть на уровень абстракции выше, написав функцию типа string GetErrorPageRelativeAddress(), или не писать её как const и написать при этом — в том числе — функцию, устанавливающую относительный путь: void SetErrorPageRelativeAddress(), а затем, прописав всевозможные действия с различными страницами, сгруппировать всё это в класс наподобие ActionTo_ServerPages{}, что уже способствует по крайней мере организованности кода и будет наталкивать на мысли, (а это — хорошо), тогда ка просто дефайном мы просто заменяем одни символы на другие, что в перспективе работы с кодом не даёт никакого выигрыша.
Так и есть, однако с препроцессором тоже легко гарантировать, что будет одна единственная константа ибо в опциях компилятора, точнее линкера, есть опция «merge duplicate string», которая для релиза везде включена по умолчанию, а в некоторых компиляторах она вообще не отключаемая.

p.s: ниже расписал почему с static const string не так всё радужно в плане инициализации.
Слияние-то есть, но от него не легче. Ибо если выбирать между нагрузкой на память и читабельностью кода, то второе предпочтительнее, потому хотя бы, что ёмкость носителей растёт, а вот мозги человеческие как были подвержены ошибкам, так и остаются. Экономия на спичках здесь не к месту, как по мне.
Иное дело, если бы речь шла о критичной к ресурсам, сложной системе реального времени, о ПО для супер-компьютера, все мощности которого, до единого байта, должны быть направлены на решение поставленной задачи. А в данном случае куда дальновиднее было бы думать о развитии проекта, что и предлагается. Ответ на код ниже будет, соответственно, ниже.
Коварное зло это мутабельные глобальные объекты, а не константы.
Почитайте что такое статическая инициализация (конкретно про constant-initialization) в C++.
Что такое статическая инициализация мне известно, благодарю, однако со сложными объектами, в т.ч. со строками она не работает если явно не использовать литералы, вот код для примера:
#include <iostream>
#include <array>
#include <string>
using namespace std;

constexpr int foo()
{
    return 5;
}

class conststr {
    const char * p;
    size_t sz;
 public:
    template<size_t N>
    constexpr conststr(const char(&a)[N]) : p(a), sz(N-1) {}
 
    constexpr char operator[](size_t n) const {
        return n < sz ? p[n] : throw out_of_range("");
    }
    constexpr size_t size() const { return sz; }
};

static const string g_static_const_str = "g456abc";

constexpr conststr g_constexpr_str = "123fgtr";

int main()
{
   array<int, foo()> a1;
   array<int, g_constexpr_str.size()> a2;
   array<int, g_static_const_str.size()> a3;
   cout << "ok" << endl;
   
   return 0;
}

Вывод:
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1
main.cpp: In function ‘int main()’:
main.cpp:32:26: error: call to non-constexpr function ‘std::basic_string<_CharT, _Traits, _Alloc>::size_type std::basic_string<_CharT, _Traits, _Alloc>::size() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int]’
    array<int, g_str.size()> a3;
                          ^
main.cpp:32:26: error: call to non-constexpr function ‘std::basic_string<_CharT, _Traits, _Alloc>::size_type std::basic_string<_CharT, _Traits, _Alloc>::size() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int]’
main.cpp:32:27: note: in template argument for type ‘long unsigned int’ 
    array<int, g_str.size()> a3;
                           ^
main.cpp:32:31: error: invalid type in declaration before ‘;’ token
    array<int, g_str.size()> a3;
                               ^

Тестировал тут.
Не понятно что за g_str, о которой говорится в сообщении об ошибке. Помимо этого, вы инициализируете свои строки не через прямую инициализацию, а через копирующую. Но по идее если переменная объявляется constexpr, а проинициализирована как constexpr быть не может, должна быть ошибка компиляции на строке с объявлением переменной.
gist.github.com/DieHertz/9871427
Все работает. Если Вы ставили акцент на то, что константная инициализация не работает с std::string — тут никто и не спорит, у std::string нет constexpr конструкторов или функций членов. Строковые константы отлично хранятся в виде const char_type* и не несут с собой никакой динамической инициализации.
Здесь речь идёт видимо о том, что в первом случае из трёх constexpr-функция foo() никаких ошибок не вызывает; во втором, когда работа идёт с самописным классом conststr, никаких ошибок тоже нет. Так?
И только в третьем случае, когда работа идёт как раз со static const string, компилятор жалуется на вызов не-constexpr функции long unsigned int std::basic_string::size(). И вроде бы — на первый взгляд — всё логично: если это не работает, значит static const string — это плохо. Есть только одно но.
По тексту ошибки компилятор не указывает ни на static, ни на const используемой строки, жалуясь именно на не-constexpr у long unsigned int std::basic_string::size(), то есть ошибка порождена именно особенностями примера, а не самой static const string.
В английском нету глаголов writed и readed, исправьте, пожалуйста. Ну очень глаза режет!
write — wrote — written
read [ri:d] — read [red] — read [red]
НЛО прилетело и опубликовало эту надпись здесь
На баше, чего уж там.
На PHP короче:)
php -S localhost:8000
НЛО прилетело и опубликовало эту надпись здесь
nc -lk 8000?
Не могли бы вы пояснить, зачем вы объявляете лямбду и тут же её вызываете? Чего вы пытаетесь добиться?
Пример странного кода:
[listen_sd](const char on){
    setsockopt(listen_sd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
    setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
}(1);
Чтобы не объявлять переменную.
Насколько я понимаю именно для этого изначально придумано лямбда исчисление.
А мне понравилось. Выглядит интереснее чем заводить переменную on в внешнем scope.

Хотя я бы сделал так:

{
  const char on = 1;
  setsockopt(listen_sd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
  setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
}
Что-то я не понял, а GCC гарантирует что время жизни этого литерала будет долгим, что вы от него указатель берете?

UPD: хотя внутри вызова функции-то можно, так как до конца строки временный объект всё равно не уничтожится
В C++ лямбды используются в основном, чтобы из передать куда-то в качестве коллбэка. Пример:

double a = 5.0;
double b = 10.0;
int count = std::count_if(std::begin(v), std::end(v), [a, b](double x){return x > a && x < b;});
В основном пока да.
Но никто не запрещал их использовать и для других целей: таких как в Haskell например.
Лямбда-исчисление было придумано для того, чтобы можно было описывать произвольные вычисления на языке, формальное описание которого состоит из нескольких строк.

О столь простом языке гораздо проще вести математически точные рассуждения.

О прихотях программистов на С++ (или других языках) писать код покороче в то время не думали, ибо С++ ещё не был создан. Запись любого алгоритма в чистом лямбда-исчислении как раз безумно громоздка, но зато синтаксис очень прост.
А чем ваш велосипед (я про server.h) лучше, чем, например, boost.asio? До тех пор, пока вы называли это примером работы с OpenSSL — ок, но сейчас вы называете это библиотекой.
А чем ваш велосипед (я про server.h) лучше, чем, например, boost.asio?

размером
Эта фраза была актуальна лет десять назад, может быть, но не сейчас. Опять же, какой размер лучше — тот, что меньше, или тот, что больше? Размер чего мы сравниваем? Исходного кода? Получившегося приложения? Головной боли по поддержке кода?
>> Эта фраза была актуальна лет десять назад, может быть, но не сейчас
[sarcasm]
Ну да, зачем сдерживаться… Винда толстеет с каждой версией, так давайте не будем отставать от столь успешного продукта!
А ещё лучше считать 2+2 через .NET 4.5 (многие утилитки такое любят). Мой любимый вид программ, которые весят 2кб, и требуют фреймворка на полсотни метров, а то и больше.
[/sarcasm]
Я как-то поставил себе Delphi XE3, запустил, подивился экзешнику с пустой формой на 15 мегабайт, и снёс нафиг. Работаю в 7 версии — и головной боли нет, и размеры адекватные.
По-моему сравнение boost.asio и целого .Net это очень сильное преувеличение.
Если рассматривать частный случай — согласен. Но уже не раз сталкивался с этим подходом, так что мой ответ скорее «общий».
Да все это обычно не имеет значения, потому что размеры обрабатываемых программами данных уже на несколько порядков превысили размер кода, так что о последнем можно даже не задумываться.
Есть небольшой линукс-сервер написанный на boost.asio. Размер бинарника: 550 K, плюс 15 K вес подлинкованной к нему библиотеки boost_system. Куда уж меньше то.
А размер исходника какой?
Размер исходников моего сервера 51.6 кБ
Размер исходников буста считать не хочу, они большие. Впрочем в этом не вижу никакой проблемы, так как для конечного пользователя продукта их все равно что нет — размер полученного бинарника я ведь привел.
Всего в три раза больше моего. Круто.
А CGI там есть? Или что он вообще умеет?
Зато, возможно, он не выдает всем на обозрение свой приватный ключ?

telnet unblok.us 8085
Trying 54.204.33.57...
Connected to unblok.us (54.204.33.57).
Escape character is '^]'.
GET /../ca-cert.pem

HTTP/1.1 200 OK
Content-Length: 1953

-----BEGIN CERTIFICATE-----
MIIC5TCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQQFADBcMQswCQYDVQQGEwJBVTET
MBEGA1UECBMKUXVlZW5zbGFuZDEaMBgGA1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQx
HDAaBgNVBAMTE1Rlc3QgUENBICgxMDI0IGJpdCkwHhcNOTkxMjAyMjEzODUxWhcN
MDUwNzEwMjEzODUxWjBbMQswCQYDVQQGEwJBVTETMBEGA1UECBMKUXVlZW5zbGFu
ZDEaMBgGA1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQxGzAZBgNVBAMTElRlc3QgQ0Eg
KDEwMjQgYml0KTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAo7ujy3XXpU/p
yDJtOxkMJmGv3mdiVm7JrdoKLUgqjO2rBaeNuYMUiuI6oYU+tlD6agwRML0Pn2JF
b90VdK/UXrmRr9djaEuH17EIKjte5RwOzndCndsjcCYyoeODMTyg7dqPIkDMmRNM
5R5xBTabD+Aji0wzQupYxBLuW5PLj7ECAwEAAaOBtzCBtDAdBgNVHQ4EFgQU1WWA
U42mkhi3ecgey1dsJjU61+UwgYQGA1UdIwR9MHuAFE0RaEcrj18q1dw+G6nJbsTW
R213oWCkXjBcMQswCQYDVQQGEwJBVTETMBEGA1UECBMKUXVlZW5zbGFuZDEaMBgG
A1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQxHDAaBgNVBAMTE1Rlc3QgUENBICgxMDI0
IGJpdCmCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBb39BRphHL
6aRAQyymsvBvPSCiG9+kR0R1L23aTpNbhXp2BebyFjbEQYZc2kWGiKKcHkNECA35
3d4LoqUlVey8DFyafOIJd9hxdZfg+rxlHMxnL7uCJRmx9+xB411Jtsol9/wg1uCK
sleGpgB4j8cG2SVCz7V2MNZNK+d5QCnR7A==
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCju6PLddelT+nIMm07GQwmYa/eZ2JWbsmt2gotSCqM7asFp425
gxSK4jqhhT62UPpqDBEwvQ+fYkVv3RV0r9ReuZGv12NoS4fXsQgqO17lHA7Od0Kd
2yNwJjKh44MxPKDt2o8iQMyZE0zlHnEFNpsP4COLTDNC6ljEEu5bk8uPsQIDAQAB
AoGAVZmpFZsDZfr0l2S9tLLwpjRWNOlKATQkno6q2WesT0eGLQufTciY+c8ypfU6
hyio8r5iUl/VhhdjhAtKx1mRpiotftHo/eYf8rtsrnprOnWG0bWjLjtIoMbcxGn2
J3bN6LJmbJMjDs0eJ3KnTu646F3nDUw2oGAwmpzKXA1KAP0CQQDRvQhxk2D3Pehs
HvG665u2pB5ipYQngEFlZO7RHJZzJOZEWSLuuMqaF/7pTfA5jiBvWqCgJeCRRInL
21ru4dlPAkEAx9jj7BgKn5TYnMoBSSe0afjsV9oApVpN1Nacb1YDtCwy+scp3++s
nFxlv98wxIlSdpwMUn+AUWfjiWR7Tu/G/wJBAJ/KjwZIrFVxewP0x2ILYsTRYLzz
MS4PDsO7FB+I0i7DbBOifXS2oNSpd3I0CNMwrxFnUHzynpbOStVfN3ZL5w0CQQCa
pwFahxBRhkJKsxhjoFJBX9yl75JoY4Wvm5Tbo9ih6UJaRx3kqfkN14L2BKYcsZgb
KY9vmDOYy6iNfjDeWTfJAkBkfPUb8oTJ/nSP5zN6sqGxSY4krc4xLxpRmxoJ8HL2
XfhqXkTzbU13RX9JJ/NZ8vQN9Vm2NhxRGJocQkmcdVtJ
-----END RSA PRIVATE KEY-----
Connection closed by foreign host.

Ну наконец-то, я ждал этого поста )))
Небось хотели что-то более интересное сделать, чем «приватный» ключик посмотреть ;)
Как предусмотрительно я запустил сервер не из под рута )))
Какая разница, что не из под рута? Если бы на сайте был контент, его бы слили.

Тем не менее, вы бы лучше прикрыли, потому что давать возможность лазать по ФС — не есть хорошо.
Прикрою.
Я сейчас больше другим озабочен: что писать дальше — cgi или прокси? Хочу и то и другое, но не могу определиться с приориетом.
Сначала нужно исправить сделанные вам замечания. Пусть даже вы хотите переизобрести boost, вам тогда нужно это явно зафиксировать. А еще определиться с языком программирования, на котором вы пишете и style guide.

После того, как исправите замечания, можно будет подумать над архитектурой сервера, разделением по уровням. Затем провести рефакторинг, по возможности избавиться от inline #ifdef.

С тем качеством кода, который у вас сейчас, можно уже и на серьезную уязвимость попасть, если добавить cgi.
Эта фраза была актуальна лет десять назад, может быть, но не сейчас. Опять же, какой размер лучше — тот, что меньше, или тот, что больше? Размер чего мы сравниваем? Исходного кода? Получившегося приложения? Головной боли по поддержке кода?


Мне буст не нравится тем, что его исходники практически нереально понять. То есть можно конечно, но для этого придется на месяц-другой отложить всю остальную работу.
В моем исходнике 517 строк кода. Чтобы в нем разобраться даже не сильно квалифицированному специалисту потребуется час-полтора от силы.
Зачем разбираться в исходниках спросите?
Чтобы править их под свои нужды, ответит Капитан Очевидность.
> В моем исходнике 517 строк кода. Чтобы в нем разобраться даже не сильно квалифицированному специалисту потребуется час-полтора от силы.

Если необходимо разобраться только в http_server.h — возможно.
Однако класс CServer в заголовочный файл не входит, и в нем надо тоже долго разбираться. Особенно долго придется разбираться из-за epool. Мало кто наизусть помнит какие методы надо вызывать в критической секции, какие нет.

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

> Чтобы править их под свои нужды

Что именно вас не устраивало в Boost.Asio что вы решили его дописать?
Если необходимо разобраться только в http_server.h — возможно.
Однако класс CServer в заголовочный файл не входит, и в нем надо тоже долго разбираться.


http_server.h — 115 строк
server.h (класс CServer) — 517 строк

Что именно вас не устраивало в Boost.Asio

Его громоздкость: огромное количество кода, в котором придется разбираться если шаг вправо-влево от документированных возможностей.
Моя библиотека устанавливается на голый Линукс-хостинг за минуту закачиванием одного файла по фтп.
Как долго и с какими граблями нужно устанавливать буст я честно говоря не помню, а теперь для программирования серверов мне это и не нужно.
«Голый линукс-хостинг», я полагаю, на базе какого бы то ни было дистрибутива работает. Попробуйте использовать пакетный менеджер этого дистрибутива для установки требуемых библиотек Boost, хотя никто не запрещает на скорую руку откомпилировать его (достаточно тривиальная задача, подробно описанная в документации).
Размером тоже иногда бывает актуально, именно когда это очень простой веб сервер :) К примеру ради простенькой веб морды к простому же приложению можно и мелочь использовать. Кстати да — ардуины ещё всякие есть, и ворох прочих контроллеров, там размер кода имеет значение.
p.s: размер кода получившегося приложения тоже важен, ибо кеши не бесконечные :)
Сомнительно выглядит передача shared_ptr по значению там, где копия не требуется. Ваш enum с типами сообщений больше по использованию должен быть scoped enum.
> explicit CClient(const CClient &) {} //Нам не понадобится конструктор копирования для клиентов
Для таких целей есть = delete, либо явное объявление без определения. Вы в противоположность комментарию объявили конструктор копирования, пуская и private.
Для таких целей есть = delete

Спасибо, не знал.
Попробовал "= delete" в Visual Studio 2012 — не компилируется, придется оставить explicit
Фишка то не в explicit, а в том чтобы определения не было. Explicit конструктор копирующий препятствует лишь копи-инициализации в форме type copy = init;
При передаче объектов типа по значению в качестве аргументов к вызову функции explicit конструкторы применяются компилятором.
Согласен, уберу определение. Не знал что без определения тоже скомпилируется.
Определение требуется только если сущность используется (odr-used, за точным определением нужно обратиться к стандарту), иначе будет ошибка компиляции, а смысл как раз таки в том, чтобы никто не пользовался.
Если хотите запрерить конструкторы или другие функции — поместите их в private: и только задекларируйте их. Оставьте их без кода, например:
private:
CClient(const CClient &);

Таким образом у вас будет ошибка компиляции ( или линковки ) при попытке кода воспользоваться этой функцией.
Хотя лучше конечно перейти на более современные версии компиляторов и пользоваться опцией delete для этого.
А ещё лучше использовать boost::noncopyable или аналогичный велосипед, (кстати есть одна бага — VS2010 классы пустышки не выкидывает :( ).
Имхо обилие энумов в C++ коде — признак того что ООП автором не понят — пишем на C используя C++.
И код на boost::asio хттп сервера будет пожалуй даже короче. :)

Эээ… А чем enum не угодил?
Энум это сериализация состояния, а далее чтобы это состояние «достать» — его надо диспатчить через if() или switch(). Т.е. это вид полиморфизма такой исторический.

Особенно хорошо это видно на функциях OnRead и OnWrote — switch() во всем своем великолепии. Совершенно бесполезная сериализация состояния имхо и заодно попытка заимплементить свой велосипедик по диспатчу. А что мешает использовать теже лямбды по прямому назначению (а не как «украшательства»)? В бусте когда постится асинхронный read/write можно спокойно передать лямбду напрямую из нужного состояния и забыть про енумы и switch() из мира C. Но так как в сабжевой либе это не учтено, все проходит через бутылочное горлышко OnRead, и собственно начинаем воевать сами с собой.

Немного дополню: можно даже без лямбд реализовать сериализацию используя возврат std::function для C++11 (boost::function для С++03).
Прочел комментарии, сделал вывод: С++ разработчики более солидарны, чем PHP разработчики.
Запостил библиотеку для работы с HTTP протоколом, по всем ООП PHP канонам, написанную «just for fun», комментарии: велосипед, есть куча аналогов, чем твой код лучше, «Вроде и все правильно, но есть ощущение хардкода» и т.д. Пост в минусах.

А у вас, каждый советует, как сделать лучше, ну несколько только уперлись в boost::asio.
Как по мне. лучше если ты сам знаешь различия, разбираешься в механизмах асинхронного ввода-вывода (epoll, IOCP), чем слепо заучиваешь функциональность готовых библиотек.

Товарищи, учитесь, развивайтесь, пробуйте и пишите красивые велосипеды!
И чем «слепо заучивать» epoll и IOCP лучше, чем «слепо заучивать» boost::asio?
Перечитай, «разбираться» != «слепо заучивать»
то есть в epoll и IOCP можно разбираться, а boost::asio — только заучивать?
Пожалуйста упростите данный код :)

            //Добавляем в начало ответа http заголовок
            std::ostringstream strStream;
            strStream << 
                "HTTP/1.1 200 OK\r\n"
                << "Content-Length: " << m_nFileSize << "\r\n" <<
                "\r\n";

            //Запоминаем заголовок
            pvBuffer->resize(strStream.str().length());
            memcpy(&pvBuffer->at(0), strStream.str().c_str(), strStream.str().length());


memcpy тут логичнее заменить на std::move, а тормозной std::ostringstream на std::string.

Так же не совсем понимаю зачем shared_ptr<vector>, ну т.е. зачем shared_ptr а не unique_ptr… или вообще ссылка на объект vector? :)

Помимо этого для:
map<string, string> m_mapHeader;

для данного кода гораздо лучше использовать enum, ну т.е. как то так:
enum Type
{
  METHOD,
  PATH,
}
map<Type, string> m_mapHeader;


Просто для информации (к текущему коду не относится): для хоть сколько нибудь значительного количества строк в коллекции использовать строку в качестве ключа не желательно из-за крайне медленных операций с ними (по сравнению с числами), поэтому если надо сделать ключом строку необходимо использовать unordered_map, что бы в качестве ключа выступал хеш (число) строки (это делается прозрачно для программиста, исключением являются пользовательские типы, для которых необходимо писать хешер), подробнее тут. Кстати говоря больших чисел (больших чем разрядность платформы*) это тоже касается, но проблема стоит уже не так актуально, т.е. если говорить в цифрах — производительность на больших числах проседает в разы, на строках — на порядки.
* тут правда не всё однозначно, ибо для x86, к примеру, есть расширения процессора, способные быстро работать с большими числами, чем базовая разрядность архитектуры.

Ну и совсем позанудствую: ведь http текстовый протокол, почему бы в качестве буферов сразу не использовать string (указав компилятору как они должны быть реализованы, ну т.е. string is unsigned char (почти везде по умолчанию уже так и есть) ), ведь тогда даже манипуляций с памятью производить не придётся.

p.s: про остальное вроде уже написали.

p.p.s: вдруг понял вот что: а у Вас точно http 1.1 реализован, не 1.0? Ибо я не вижу поддержки обработки запроса кусков файла, к примеру.
p.p.p.s: экспериментируйте, тренеруйтесь, глядишь и правда получится очень маленький и простой веб сервер.

Пойду ка я спать пока какую нибудь ерунду писать не начал ^_^
memcpy тут логичнее заменить на std::move, а тормозной std::ostringstream на std::string.

В комментариях к предыдущим моим статьям мне убедительно доказали, что для компилятора std::memcpy и std::copy — одно и тоже.
std::ostringstream удобнее для формирования строки где присутствуют не строковые типы («Content-Length» в примере).

зачем shared_ptr а не unique_ptr

мне первый больше нравится и кажется более безопасным в использовании.
Для буфера также удобно использовать именно объект вектор — так не нужно везде еще и размер передавать.

надо сделать ключом строку необходимо использовать unordered_map

спасибо, буду знать.

ведь http текстовый протокол

Нет. По нему можно и двоичные данные передавать тоже.
В комментариях к предыдущим моим статьям мне убедительно доказали, что для компилятора std::memcpy и std::copy — одно и тоже.

Агу, но я говорил про замену на std::move

std::ostringstream удобнее для формирования строки где присутствуют не строковые типы («Content-Length» в примере).
Есть же std::to_string.

Для буфера также удобно использовать именно объект вектор — так не нужно везде еще и размер передавать.

В string тоже размер встроен, в том то и дело ;)

Нет. По нему можно и двоичные данные передавать тоже.

Это понятно, я имел ввиду, что сам протокол текстовый, ну т.е. не бинарный :) ибо заголовки, ответы, и т.д. легко читаются человеком. Стало быть поскольку работать со строками всё равно придётся постоянно, то лучше буфер сделать строкой, что бы туда сюда просто так не копировать память.
Есть же std::to_string.

Буду знать, спасибо еще раз ))

Про string vs vector буду думать.
Сейчас набегут люди и будут хвастаться своими веб-серверами на голом C в 30 строк ;-)
Не набегут.
1. Перфоманс уже прошёл.
2. Опыт товарищей с JavaScript, кучей минусов за посты и в карме.
3. Люди, компетентные в Си, подобным поведением не страдают.
:-)
Ну… я бы, в принципе, мог «набежать» и показать веб сервер, хранящий странички в flash памяти контроллера, и передающий их прямо оттуда, без копирования данных в ОЗУ через самодельный printf, который это умеет, и подставляет в страничку недостающие данные. При этом всё это безобразие успешно работает вместе с остальным софтом, обработчиками прерываний, и т.д. на контроллере с 2 кБ ОЗУ, но KrD верно заметил, что профита от рассказов на хабре о таких извращениях особого нету :)
я тут немножко потыкал unblok.us:8085… там канал такой плохой или оно дико медленное?
там канал такой плохой или оно дико медленное

Да вроде нечему там особо тормозить. А хостинг Амазон микроинстанс. Процессор тормоз, памяти кот наплакал и канал из враждебной омерики )))
побенчмаркал я вашу поделку… в лучшем случае получается 7к реквестов в секунду, это отвратительно мало… для сравнения jetty выдает примерно 70к rps на той же машине
мой хостин яву не потянет, я установил nginx.
Результаты бэнчмарка index.html:

Мой сервер:
image

nginx


Хотя раз на раз не приходится. Написал, потом еще раз проверил свой:

это без keep-alive чтоли?
Не знаю. Только установил nginx, ничего не менял в настройках.
какие результаты с ab -n 1000 -c 50 -k?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории