Возможно ли это?
Да.
Знаю, звучит странно и больше похоже на упражнение в бесполезности, но это не так.
В этой статье я расскажу, как можно использовать С++ в разработке сайта, и приведу несколько достойных причин этому.
Будет не только интересно, но также полезно и очень практично.
Хостинг
Вы наверняка думаете: такая необычная конфигурация может быть реализована только в специализированной среде. Однако это, как правило, не так. Несмотря на то, что обычный или виртуальный специальный сервер был бы идеальным решением для множества сайтов (не только сайтов на С++), в большинстве случаев можно использовать С++ на общих хостингах.
Любые веб-хостинги, поддерживающие 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++ для вашего следующего нишевого проекта в вебе.