Почему обычный HTTP-запрос — это как письмо по почте, а SSE — телеграмма в реальном времени
Представьте: вы ждёте уведомление о новом комментарии на сайте. Вы обновляете страницу каждые 5 секунд. Сервер каждый раз возвращает кучу HTML, CSS, картинок — и только потом, в самом низу, вы видите: «Новых комментариев нет». Знакомо? Это называется polling. Работает, но жрёт трафик и грузит сервер.
А теперь представьте другой сценарий: вы открыли страницу, и через секунду на ней появляется сообщение: «Василий оставил комментарий: "Классная статья!"». Без перезагрузки, без лишних запросов. Сервер сам «толкает» вам данные. Это и есть Server-Sent Events (SSE) — технология, которая позволяет серверу отправлять клиенту поток событий через обычное HTTP-соединение.
В этой статье мы разберём, как реализовать SSE на чистом C++ без использования тяжёлых фреймворков. Вы узнаете, почему Server-Sent Events в C++ — это отличный выбор для мониторинга, биржевых котировок, чат-уведомлений и любых задач, где нужна односторонняя передача данных в реальном времени.
Как работают Server-Sent Events? Разбор протокола
SSE — это часть спецификации HTML5. Клиент открывает обычное HTTP-соединение и говорит серверу: «Я готов слушать события». Сервер в ответ не закрывает соединение, а начинает отправлять данные в специальном формате text/event-stream.
Формат сообщения предельно простой:
data: Это текст события\n\n
Каждое событие начинается с data: и заканчивается двойным переносом строки. Можно добавить id: для идентификации, event: для типа события и retry: для указания времени переподключения в миллисекундах.
Пример полноценного потока:
id: 42
event: new_message
data: {"user": "Василий", "text": "Привет!"}
id: 43
event: ping
data: Сервер жив
Клиент (браузер или любое приложение) обрабатывает эти строки и генерирует JavaScript-события. Всё гениальное — просто.
SSE vs WebSocket: когда что выбирать?
Многие сразу думают о WebSocket, когда речь заходит о реалтайме. Давайте сравним:
- WebSocket — двусторонняя связь. Клиент и сервер могут инициировать обмен. Требует отдельного протокола (ws://), прохождения через прокси, часто блокируется корпоративными файрволами.
- SSE — односторонняя связь (сервер → клиент). Работает поверх обычного HTTP. Автоматически переподключается при разрыве. Легко проходит через любые прокси.
Если ваша задача — отправлять уведомления клиенту, а не ждать от него команд, Server-Sent Events на C++ — это элегантное и простое решение. Не нужно тащить тяжелые библиотеки, не нужно писать сложные хендлеры.
Пишем SSE-сервер на C++ с нуля
Для реализации нам понадобится минимум: сокеты и работа с HTTP. В реальных проектах используют Boost.Beast или libmicrohttpd, но для понимания сути мы напишем простой сервер на чистом C++ с использованием POSIX-сокетов.
Наш сервер будет:
- Принимать HTTP-запрос от клиента
- Определять, что клиент хочет SSE (по заголовку
Accept: text/event-stream) - Отправлять поток событий с текущим временем каждые 2 секунды
Шаг 1: Создаём сокет и слушаем порт
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <thread>
#include <chrono>
#include <sstream>
void handle_client(int client_socket) {
// Буфер для запроса
char buffer[4096] = {0};
read(client_socket, buffer, 4096);
// Проверяем, что это GET запрос с Accept: text/event-stream
std::string request(buffer);
if (request.find("GET /events") == std::string::npos) {
// Отправляем простой ответ для других запросов
std::string response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nServer is running";
send(client_socket, response.c_str(), response.size(), 0);
close(client_socket);
return;
}
// Отправляем SSE-заголовки
std::string headers =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/event-stream\r\n"
"Cache-Control: no-cache\r\n"
"Connection: keep-alive\r\n"
"Access-Control-Allow-Origin: *\r\n"
"\r\n";
send(client_socket, headers.c_str(), headers.size(), 0);
// Отправляем события каждые 2 секунды
int event_id = 0;
while (true) {
std::stringstream ss;
ss << "id: " << event_id++ << "\n";
ss << "event: time_update\n";
ss << "data: {\"time\": \""
<< std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())
<< "\"}\n\n";
std::string event = ss.str();
if (send(client_socket, event.c_str(), event.size(), 0) < 0) {
// Соединение разорвано или ошибка
break;
}
std::this_thread::sleep_for(std::chrono::seconds(2));
}
close(client_socket);
}
int main() {
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);
bind(server_fd, (sockaddr*)&address, sizeof(address));
listen(server_fd, 3);
std::cout << "SSE Server started on port 8080" << std::endl;
while (true) {
int client_socket = accept(server_fd, nullptr, nullptr);
std::thread(handle_client, client_socket).detach();
}
close(server_fd);
return 0;
}
Этот код — минимальная, но рабочая реализация SSE сервера на C++. Запустите его, откройте браузер на http://localhost:8080/events — и вы увидите поток событий.
Шаг 2: Клиентская часть (HTML + JavaScript)
Чтобы протестировать сервер, создайте простую HTML-страницу:
<!DOCTYPE html>
<html>
<head>
&