Возможно ли это?
Да.
Знаю, звучит странно и больше похоже на упражнение в бесполезности, но это не так.
В этой статье я расскажу, как можно использовать С++ в разработке сайта, и приведу несколько достойных причин этому.
Будет не только интересно, но также полезно и очень практично.
Хостинг
Вы наверняка думаете: такая необычная конфигурация может быть реализована только в специализированной среде. Однако это, как правило, не так. Несмотря на то, что обычный или виртуальный специальный сервер был бы идеальным решением для множества сайтов (не только сайтов на С++), в большинстве случаев можно использовать С++ на общих хостингах.
Любые веб-хостинги, поддерживающие CGI (то есть, в принципе, все), смогут поддерживать и сайт на С++. В зависимости от провайдера, вы или сможете локально скомпилировать свой сайт, или нет, и вам понадобится компилятор, включённый в ваш аккаунт. Проверьте их, если вы планируете редактировать и компилировать через SSH прямо на веб-сервере.
Простой пример
В приведённых примерах я использую учетную запись хостинга cPanel. Они доступны, недороги и последовательны. Но вы можете легко адаптировать эти методы к виртуальному или выделенному серверу или экземпляру Amazon EC2 с помощью нескольких простых модификаций конфигурации Apache.
cPanel предоставляет нам папку cgi-bin, но нам она не нужна. В большинстве случаев любой файл с расширением .cgi будет автоматически обрабатываться, если он имеет правильные разрешения (обычно 0755). Вот необходимые файлы (обязательно используйте TAB в Makefile)
Makefile:
all:
g++ -O3 -s hello.cpp -o hello.cgi
clean:
rm -f hello.cgi
hello.cpp:
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;
void set_content_type(string content_type) {
cout << "Content-type: " << content_type << "\r\n\r\n";
}
void set_page_title(string title) {
cout << "<title>" << title << "</title>\n";
}
void h1_text(string text) {
cout << text << "\n";
}
int main() {
set_content_type("text/html");
// Output HTML boilerplate
cout << "<!doctype html>\n";
cout << "<html lang=\"en\">\n";
cout << "<head>\n";
set_page_title("Hello, World!");
cout << "</head>\n";
cout << "<body>\n";
h1_text("Hello, World!");
cout << "</body>\n";
cout << "</html>";
return 0;
}
Если вы располагаете компиляторами, включёнными в аккаунт (возможно, понадобится спросить команду поддержки вашего веб-хостинга), просто используйте SSH в своём аккаунте, поместите эти файлы в папку public_html, затем выполните:
make
Будет выполнен файл hello.cgi. Если вы введёте адрес файла в браузере, например, так:
http://your-test-site.com/hello.cgi
То замените your-test-site.com на своё доменное имя или URL хостинга. На экране должно появиться «Hello World»
Прежде, чем погрузиться в сам код, давайте рассмотрим, как это работает на веб-сервере. Когда Аpache принимает запрос, первым делом он смотрит на внутренний обработчик или на правило перезаписи, затем ищет на диске файл, соответствующий запросу. В нашем случае он находит hello.cgi и извлекает его. Наша программа не принимает входных данных, а только выводит сообщение «Hello world». Затем Apache принимает эти данные и возвращает их пользователю.
Говоря о коде, следует упомянуть, что он мог быть и проще. Необязательно было включать отдельные функции set_content_type, set_page_title и h1_text. Есть простые помощники, поддерживающие чистоту в основной функции. Всё это можно просто вывести в основную функцию, и она будет работать точно так же.
Но я надеюсь, вы видите преимущество в выделении этих функций. Если бы вам понадобилось создать функцию для каждого HTML-элемента, вы могли бы использовать вот такой код для создания чистого ответа отклика в своей программе:
void p(string text) {
cout << "<p>" << text << "</p>\n";
}
Затем использовать что-то вроде:
p("This would be paragraph text.");
Для вывода абзаца.
Вы могли бы даже развить эту идею, чтобы вспомогательные функции, такие как p, h1_text и т.д., возвращали текст вместо прямого вывода в стандартный вывод через cout. При этом вы можете создать систему шаблонов или вкладывать ответы для создания сложных страниц с очень обтекаемым и эффективным кодом C ++.
Этот пример максимально простой, указывает только на основные моменты. У вас есть полный доступ к каждому заголовку ответа, что дает вам полный контроль над циклом ответа.
Ещё о входных данных
Наш пример не принимает никаких входных данных, оно лишь возвращает «Hello World». Но на каждый запрос Apache отвечает программе огромным количеством информации через переменные среды. Можно применить функцию getenv() в стандартной библиотеке С, чтобы получить их значения (не забудьте добавить #include <stdlib.h> в начале кода).К примеру, если бы вам понадобилось узнать полный URI запроса, вы бы использовали:
string request_uri = getenv("REQUEST_URI");
чтобы получить это значение. Другие полезные переменные:
- REMOTE_ADDR — Получение IP-адреса посетителя
- REQUEST_METHOD — Возвращает метод (то есть GET, POST и т.д.)
- DOCUMENT_ROOT — Рут сайта (обычно ~ / public_html на общих системах или / var / www / html на виртуальных / выделенных серверах).
- QUERY_STRING — Строка запроса для получения переменных GET.
Более ясный пример
Безусловно, возможно разобрать переменные GET вручную, а обработка переменных POST может быть выполнена путем проверки стандартного ввода. Можно даже получать и устанавливать файлы cookie, изменяя заголовки запросов и ответов. Но оба подхода слишком утомительны.
Можно написать свои собственные оболочки или использовать уже готовую библиотеку GNU cgicc. В ней содержатся вспомогательные функции для изменения HTML и обработку форм. В работе с большими проектами использование такой библиотеки прилично сэкономило бы время.
На Debian и Ubuntu установить библиотеку и заголовки можно с помощью:
apt install libcgicc5 libcgicc5-dev
Но на CentOS / RHEL нет собственных пакетов. Чтобы установить на них, запустите:
cd /usr/local/src
wget ftp://ftp.gnu.org/gnu/cgicc/cgicc-3.2.19.tar.gz
tar xfz cgicc*.tar.gz
cd cgicc*
./configure — prefix=/usr
make
make install
ПРИМЕЧАНИЕ: 3.2.19 была самой последней версией на момент написания, но вы можете проверить наличие более новой копии на ftp.gnu.org/gnu/cgicc. Я также использовал / usr в качестве префикса, чтобы избежать проблем со связыванием библиотек. Если что, спокойно их меняйте.
После установки cgicc можете компилировать его. Попробуйте вот этот пример, который принимает входные данные из формы и выводит их в браузере:
Makefile:
all:
g++ -O3 -s hello.cpp -o hello.cgi
g++ -O3 -s cgicc.cpp -o cgicc.cgi /usr/lib/libcgicc.a
clean:
rm -f hello.cgi cgicc.cgi
cgicc.html:
<!doctype html>
<html lang="en">
<head>
<title>cgicc Test</title>
</head>
<body>
<form method="POST" action="cgicc.cgi">
<label for="name">Name</label>
<input name="name" type="text" value="">
<input name="submit" type="submit" value="Submit">
</form>
</body>
</html>
cgicc.cpp:
#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
void set_content_type(string content_type) {
cout << "Content-type: " << content_type << "\r\n\r\n";
}
void set_page_title(string title) {
cout << "<title>" << title << "</title>\n";
}
void h1_text(string text) {
cout << text << "\n";
}
int main() {
Cgicc cgi;
string name;
set_content_type("text/html");
cout << "<!doctype html>\n";
cout << "<html lang=\"en\">\n";
cout << "<head>\n";
set_page_title("cgicc Test");
cout << "</head>\n";
cout << "<body>\n";
cout << "<p>";
// Grab the "name" variable from the form
name = cgi("name");
// Check to make sure it isn’t empty.
if (!name.empty()) {
cout << "Name is " << name << "\n";
} else {
cout << "Name was not provided.";
}
cout << "</p>\n";
cout << "</body>\n";
cout << "</html>";
return 0;
}
Вы могли заметить, что я статически привязал библиотеку cgicc в Makefile. Пусть это и необязательно (можно заменить это на -Icgicc), я предпочитаю статически привязывать двоичный код, который я отправляю на сервер, чтобы всё необходимое для выполнения программы отправлялось комплектом.
В этом примере библиотека cgicc проделывает тяжёлую работу переменных POST и возвращению нам «name».
Я не избежал входной переменной POST, но это рекомендуется, особенно в местах производства, тем более, если вы взаимодействуете с базой данных.
Это лишь малая часть возможностей cgicc. Полную документацию можете прочитать здесь.
Выполнение/представление
С++ невероятно быстр в работе с хорошим кодом. Интерфейс CGI немного замедляет работу, но даже так вы получите выполнение лучше, чем на интерпретируемых языках вроде PHP.
Как говорится, всегда есть, к чему стремиться. В специализированной или виртуальной среде можно использовать поддержку FastCGI у Nginx или Apache, чтобы уменьшить небольшую (и незаметную, если сервер не сильно нагружен) задержку в момент загрузки программы. В моих тестах задержек не наблюдалось, но для работы с веб-сайтом с загруженным трафиком полезно будет изучить эти решения для ещё более быстрого выполнения/быстрой работы
Дополнительно
Было бы просто обернуть программу на С++ в тонкий Docker-контейнер. Это обеспечит вам огромную гибкость при размещении сайта. Также можете получить доступ к базе данных MySQL, включив в свою программу заголовки разработки C / C ++ MySQL. Если вы знакомы с использованием MySQL с PHP, вы заметите, что имена переменных очень похожи.
Вместо того, чтобы создавать дополнительные процессы командной строки для обработки изображений, вы можете включить заголовки ImageMagick C ++ для обработки этих изображений непосредственно в вашей программе
Какое длинное и странное путешествие
Создание сайта на C++ практично, особенно если важна производительность. Я не рекомендовал бы это для блога или личного сайта — их можно легко сделать на WordPress. Но если у вас есть крайняя потребность в скорости и желание хорошо писать по проторенному пути, рассмотрите C++ для вашего следующего нишевого проекта в вебе.
Комментарии (37)
berez
13.12.2019 22:13+3Удивительное дело. Когда-то давно я такой проект видел — только с очень большим трудом удалось уговорить его создателя перейти на какой-нибудь скриптовой язык, чтобы проект можно было реально развивать. В процессе уговоров я слышал практически все «аргументы» за С++ в вебе, которые автор приводит в статье. 15 лет прошло, а заблуждения все те же. :)
Разве что докера тогда не было.
Вот даже не поленюсь разобраться с главным:
С++ невероятно быстр в работе с хорошим кодом. Интерфейс CGI немного замедляет работу, но даже так вы получите выполнение лучше, чем на интерпретируемых языках вроде PHP.
Быстр — это прекрасно. Вот только скорость вычислений в вебе не очень-то важна. CGI-программы не занимается тяжелыми вычислениями. А сэкономленные сто тактов процессорного времени просто потеряются на фоне ожиданий и задержек, вносимых запросами к базе данных и операциями ввода-вывода.
А вот про что автор скромно умолчал:
- Страницы приходится формировать вручную чуть ли не построчно. Т.е. ни о каком редизайне сайта речи быть не может: поди разберись в тысячах строчек, где выводятся куски страниц. Если же грузить страницы из внешних файлов, то получится свой самописный шаблонизатор — т.е. тот же PHP, но обрезанный и свой.
- То, что текст всех страниц хранится в памяти, приводит к разрастанию бинаря. Чем больше разнообразных страниц отдает сайт в своих ответах, тем больше текста в бинаре. При этом на любой запрос в память грузится весь бинарь, хотя в ответе реально используется лишь малая часть текста. Тут можно возразить, что текста будет меньше при правильном разбиении страницы на хедер, футер и прочие стандартные части. Это так, но для больших проектов текста будет все равно очень много. Можно, конечно, собрать несколько разных бинарей… :)
- Если бинарь упадет по ошибке, то найти причину падения будет очень сложно. Особенно если нет core dump'а. Или нет отладочной информации.
- Если на сайт большая нагрузка, обновить бинарь не удастся. Пока программа работает, исполняемый файл блокируется от записи (не всегда, не везде, но бывает). Придется приостанавливать веб-сервер или химичить с символическими ссылками.
Это то, что сходу вспомнилось.
А, и еще один веселый момент: если сайт расположен на хостинге, то тамошний админ может сильно удивиться, обнаружив у клиента непонятный бинарь с правами запуска. Ну и удалит его нафиг для ясности. :)Fox_exe
14.12.2019 10:00Вопрос с формированием страниц решается созданием простейшего шаблонизатора…
Правда, таким макаром и до собственного интерпритатора недалеко…
Из плюсов использования C++ в вебе — Это возможность обойтись и вовсе без веб-сервера (написать свой, как часть сайта, либо воспользоваться uHttp или другим мини-сервером).
Ещё большим плюсом будет малый размер исполняемых файлов, что критично для всяких роутеров и прочим мини-девайсов.berez
14.12.2019 14:43Вопрос с формированием страниц решается созданием простейшего шаблонизатора…
Правда, таким макаром и до собственного интерпритатора недалеко…
Именно об этом я и говорю.
Из плюсов использования C++ в вебе — Это возможность обойтись и вовсе без веб-сервера (написать свой, как часть сайта, либо воспользоваться uHttp или другим мини-сервером).
Ну так-то да. Только автор статьи рассказывает именно про CGI (причем на хостингах). На хостинге за открытие порта в неположенном месте могут и по тыковке настучать. :)
mcroitor
14.12.2019 11:38Не убедительно. В любом языке страница формируется построчно или шаблонизатором. И хранить все страницы в памяти это странно. Пользуйтесь фреймворками.
berez
14.12.2019 14:46-1И хранить все страницы в памяти это странно.
А вы примеры посмотрите в статье — там все фрагменты страницы хранятся как строки. Где хранятся? Естественно, в памяти.
Если пользоваться шаблонизатором, то какой смысл писать свой, если есть уже готовый и очень неплохой — PHP называется? :)
Пользуйтесь фреймворками.
Какими? Qt — чтоб сразу +7 мегабайт в память загрузилось? :)Fox_exe
14.12.2019 15:23Если пользоваться шаблонизатором, то какой смысл писать свой, если есть уже готовый и очень неплохой — PHP называется? :)
PHP, всёж, интерпритатор.
А речь про примитивный шаблонизатор: Передача браузеру клиента HTML кода из файла, «на лету» подменяя ключевые слова (токены?) на нужный код или текст, который тоже можно подгружать из другого файла.
Правда всё это актуально на какойнить STM32 или Arduino, а на более производительных чипах, как правило, можно запустить чтото более удобное для разработки (php, nodejs/javascript, lua и т.п.)
Хотя я бы реализовал чуть иначе: На CGI — только Json API, всё остальное (html, js, css, jpg/png/svg) отдавать самим веб-сервером и делать логику по максимуму через js (Как, собственно, и делают многие современные сайты, перенося таким образом почти все вычисления на клиента)
mcroitor
14.12.2019 15:31Есть мнение, что С++ живет не только с QT. Предложите тогда решение на PHP для web сервера с 2 мб жёсткой памяти. Современный php, кстати, предлагает фреймворки на десятки мб. Я не в коем случае не ругаю php, я люблю писать на нем даже системные скрипты. Но современный c++ ничем не хуже. И годится для самых разных задач. И для веба тоже.
berez
14.12.2019 15:35Есть мнение, что С++ живет не только с QT. Предложите тогда решение...
Ответ понятен.
Но современный c++ ничем не хуже. И годится для самых разных задач. И для веба тоже.
Сейчас-то да, хотя время уже давно упущено.
Речь шла о С++ 15-летней давности. Ну и плюс к тому — гражданин писал на нем как на слегка улучшенном си — ASCIIZ строки, strcat/strlen, вот это все.
pal666
14.12.2019 15:14+2из ваших 4 пунктов ни один не описывает с++.
формировать страницы на с++ можно так же, как и на других языках, хранить текст можно в любом количестве файлов, отладочную информацию и корки иметь никто не запрещает, блокировка по записи ни на что не влияет, т.к. файл не перезаписывается при обновлении, а создается новый и переименовывается вместо старого. хотя с последним пунктом может быть сложнее несчастным пользователям виндовых вебсерверов, но опять же дело не в с++berez
14.12.2019 15:32Дело не в С++, а в том, что люди руководствуются при выборе языка странными представлениями. Ну а потом, понятно, мучаются.
Porohovnik
13.12.2019 22:52А где-то сейчас живёт форум на ассемблере, на хабре о нём статья есть: https://m.habr.com/ru/post/318916/ ...
kunix
13.12.2019 23:23Не ну смысл иногда есть.
Например, вы глубокий параноик и считаете, что весь более-менее сложный софт уязвим.
Поэтому какие-то злобные хакеры перидоческих находят зиродеи в этих ваших апачах, энджинксах, и пехапе.
И сразу же идут атаковать ваш сайт.
Поэтому вы пишете свой веб-сервер, у которого будет свой аутентичный набор уязвимостей, которые никому не известных.
Но это до первого попадания бинаря в лапы варага.
И да, HTTP запрос надо ручками парсить, а то вдруг что…DollaR84
14.12.2019 00:57Знаете, никогда не страдал паранойей, исходил из принципа — да кому я нужен, меня взламывать. Сделал маленький простенький сайт для себя, причем вообще простой, без каких-либо движков и т.д. Смотрю логи по запросам: целая куча запросов в поисках файлов wordpress типа логина и еще других, потом поиски на разных уровнях каталогов файла /etc/passwd, а потом вообще пошли SQL инъекции. Я может еще не совсем параноик, но реально достали…
AntonSazonov
13.12.2019 23:55+1Бесполезная фигня.
Хочешь сайт на C++ — напиши простейший сервер на C++ и интегрируй в него контент. Будет работать в разы быстрее.
Orange11Sky
14.12.2019 00:24А что мешает прикрутить к программе на плюсах библиотеку во встроенным веб-сервером?
Для вывода служебной статистики и диагностики вполне удобно.
jknight
14.12.2019 00:28Уже такую пятую или шестую статью на Хабре вижу. Без комментариев, насколько это полезно (уже сверху много на эту тему написали), но каждый раз удивляюсь — почему авторы сих статей отказываются рассказывать про что-то, по крайней мере, упрощающее разработку и развивающееся? Тот же https://www.webtoolkit.eu/wt, к примеру. Опять-таки, не утверждаю, что это нужно, но хотя бы на что-то эти проекты годны, в отличие от предлагаемого всеми этими авторами.
Пример хорошего использования — к примеру, делаем железку силами команда С++- программистов, нужна на ней веб-морда с настройками/результатами работы. Можно отдать на аутсорс, а можно что-то подобное попользовать.
avolver
14.12.2019 14:11Вот тоже вспомнил про github.com/emweb/wt
Можно же и просто веб-проект делать на С++, без железки.
Или вот такой микро-фреймворк имеется: pistache.io
Vyacheslav_N
14.12.2019 14:5715 лет назад видел сайт на Delphi 6. Тот же метод реализации. Можно подобное сделать на assembler, но вопрос зачем? На Ruby или Python это можно сделать в десятки раз быстрее и проще. Если требуется большая производительность то Golang. Web решение на golang возможно будет даже быстрее чем на C++. Не нужно стрелять из пушки по воробьям, нужно выбирать инструмент адекватный требованиям. Автору, спасибо за статью. Было очень интересно.
Costic
14.12.2019 18:46Лет 20 назад делал модуль на ISAPI для MS IIS, чтобы шустро с БД работать и видео-поток гнать. CGI — это недопустимо медленно. Под IIS и шаблонизаторы есть, странно что никто тут не вспоминает. MS AtlDriver/Server были, наверное фреймворк это сейчас называется, на выходе DLL была, которая в адресном пространстве IIS работала. Всё это шустро работало в одной торговой сети. Трудоёмко на С++ делать, JS проще.
BigDflz
14.12.2019 19:25если говорить о глобальном — то сайт на С мало отличается от сайта на java. jar/war или exe — по сути одно и тоже. что на С, что на java надо иметь исходники, что бы внести изменения. необходима трансляция. Только java кросплатформенное приложение, а С платформозависимое.
estet
14.12.2019 19:36Согласен с другими комментаторами, такие статьи писали десять лет назад и ничего нового она не открывает. Для тех, кому интересно, есть два фреймворка для написания FastCGI приложений на Си: Kore и kcgi. Автор kcgi к тому же занимается евангелизмом стека BCHS (BSD, C, httpd, SQLite) — learnbchs.org, любопытные доклады и статьи.
Из примеров Web приложений на Си это форум МФТИ, который написан на Си, работает уже ~20 лет — board.rt.mipt.cc. Насколько помню, до этого была такая же борда, написанная на каком-то скриптовом языке и она тормозила, поэтому переписали на Си. Исходный код есть на Гитхабе.
justhabrauser
14.12.2019 20:49Прекрасный вариант узкоспециализированного веб-сервера для «веб-хуков» — дернуть что-то там на сервере за какую-то ручку например.
Без питонов/PHP/апача — чисто под thttpd подложить (причем в голом chroot'е) и всё.
PS. А проверка на наличие POST не нужна, что ли?
johnfound
15.12.2019 01:28То что CGI скрипты нагружают больше сервера – это так. Но когда скрипт написан на компилируемым языком как C++, C или ассемблер и инициализация этого скрипта быстрая, то нагрузка не так уж и большая.
А если нельзя запускать быстро из за инициализации, то всегда можно перейти на FastCGI или SCGI.
И веб на C++ совсем не такая экзотика. Например Fossil именно так и делает весь веб интерфейс. Эта ссылка ведет именно на сайт на C++.
kunix
16.12.2019 12:20Если нужно с полпинка поднять специализированный веб-сервер, то есть вполне современный вариант на Java и Netty.
То есть, общий вывод из этого такой.
Сервить странички на C++ в 2019 году нужно только разве что, если у вас там глубокий embedded и вы занимаетеь байтоеб… ом.
DollaR84
С созданием hello world на плюсах для CGI игрался еще лет 15 назад. Но кроме этого больше и не смог придумать для чего бы реально полезного можно было бы это применить.
BigDflz
как полноправного сайта на C создавать конешно смысла большого нет, но вот как локальный «сайт» для связи браузера с локальным железом — вполне. и такие поделки есть.
berez
Лет 15 назад я видел самый настоящий проект для веба на плюсах — с базой данных и страничками, выводимыми через cout <<
Проект был… эмм… не очень надежным и постоянно падал. :)
mcroitor
Лет 20 назад на С++ с друзьями писал форум. Работало замечательно.
a-tk
А ещё на С++ можно написать PHP (или что угодно другое) и больше не страдать ерундой.