Skip to content

WebSocket-ответы

Формирование и отправка кадров обратно клиенту через WebSocket-соединение. Ответ доступен через объект wsctx_t->response (websocketsresponse_t*). Методы ответа — это указатели на функции, навешанные на объект, поэтому первым аргументом всегда передаётся сам объект.

c
#include "websockets.h"

void my_handler(wsctx_t* ctx) {
    ctx->response->send_text(ctx->response, "Hello World");
}

Протокол WebSocket передаёт данные кадрами двух содержательных типов — текстовые (opcode 0x81) и бинарные (opcode 0x82). Фреймворк предоставляет методы для каждого формата, а также общий метод, который выбирает формат по типу входящего запроса.

Когда отправляется ответ

Кадр формируется и ставится в очередь отправки только при вызове одного из методов send_*. Пока метод не вызван, клиенту ничего не уходит — поэтому в конце обработчика обязательно нужно вызвать send_text/send_binary/send_data/send_file (или управляющий кадр). Если ни один метод не вызвать, клиент не получит ответа по этому запросу.

После отправки ответа обработчик должен завершиться (return) — повторная отправка в рамках одного запроса сформирует некорректный кадр.

Обзор методов

МетодАргументыОписание
send_text(response, data)Текстовый кадр, null-terminated строка
send_textn(response, data, length)Текстовый кадр с явной длиной
send_binary(response, data)Бинарный кадр, длина через strlen
send_binaryn(response, data, length)Бинарный кадр с явной длиной
send_data(ctx, data)Кадр в формате запроса (текст/бинарный)
send_datan(ctx, data, length)То же с явной длиной
send_file(response, path)Файл бинарным кадром, путь относительно корня
send_filen(response, path, length)То же с явной длиной пути

Первый аргумент отличается

Большинство методов принимают объект ответа — ctx->response. Исключение — send_data/send_datan: им нужен весь контекст ctx, чтобы прочитать тип входящего запроса. Поэтому вызов выглядит как ctx->response->send_data(ctx, ...).

Текстовые сообщения

send_text отправляет null-terminated строку текстовым кадром. Если данные содержат встроенные нули или их длина известна заранее, используйте вариант send_textn с явной длиной:

c
void get(wsctx_t* ctx) {
    ctx->response->send_text(ctx->response, "{\"message\": \"This is json\"}");
}

void getn(wsctx_t* ctx) {
    const char data[] = { 'H', 'i', 0x00, '!' };
    ctx->response->send_textn(ctx->response, data, sizeof(data));
}

Бинарные сообщения

send_binary отправляет бинарный кадр. Важно: этот вариант определяет длину через strlen(), поэтому он небезопасен для данных со встроенными нулями — всё после первого нулевого байта будет потеряно. Для настоящего бинарного содержимого используйте send_binaryn с явной длиной:

c
// Безопасно для текста без нулей
void get(wsctx_t* ctx) {
    ctx->response->send_binary(ctx->response, "Text in binary format");
}

// Безопасно для произвольных байтов
void raw(wsctx_t* ctx) {
    const char data[] = { 0x00, 0x01, 0x02, 0x03, 0x00, 0xFF };
    ctx->response->send_binaryn(ctx->response, data, sizeof(data));
}

Автоматический формат

Метод send_data определяет тип входящего запроса и отправляет ответ в том же формате: если запрос был текстовым — уходит текстовый кадр, иначе бинарный. Удобно для echo-обработчиков и случаев, когда формат ответа должен совпадать с форматом запроса.

c
void echo(wsctx_t* ctx) {
    ctx->response->send_data(ctx, "echo");          // null-terminated
    ctx->response->send_datan(ctx, "echo", 4);      // с явной длиной
}

Один формат на соединение

WebSocket не требует, чтобы запрос и ответ были в одном формате, но на практике удобнее держать их согласованными — так клиентский парсер всегда знает, чего ждать. send_data снимает с разработчика ручную проверку типа запроса.

Отправка файлов

send_file читает файл и отправляет его одним бинарным кадром. Путь указывается относительно корня сервера (server.root из config.json), аналог с явной длиной пути — send_filen:

c
void get(wsctx_t* ctx) {
    ctx->response->send_file(ctx->response, "files/image.jpg");
}

Метод возвращает 0 при успехе и -1 при ошибке. При этом ошибочный случай не остаётся без ответа: если файл не найден или это каталог, автоматически отправляется текстовое сообщение ("resource not found" / "resource forbidden"). Отдельно вызывать send_text или send_binary вместе с send_file не нужно — это лишь повредит кадр.

c
void get(wsctx_t* ctx) {
    int result = ctx->response->send_file(ctx->response, "files/report.pdf");

    if (result == -1) {
        // Файл не найден или недоступен — текстовый кадр с ошибкой уже отправлён.
        return;
    }
}

Управляющие кадры

Помимо текстовых и бинарных, протокол определяет управляющие кадры. Для их отправки используются свободные функции (не методы объекта), объявленные в websocketsresponse.h:

ФункцияOpcodeНазначение
websocketsresponse_pong(response, data, length)0x8AОтвет на входящий PING; обычно эхо полезной нагрузки пинга
websocketsresponse_close(response, data, length)0x88Инициировать закрытие соединения; data — необязательная причина/код
c
void close_handler(wsctx_t* ctx) {
    // Закрыть соединение с кодом 1000 (Normal Closure)
    const char reason[] = { 0x03, 0xE8 }; // 1000 в big-endian
    websocketsresponse_close(ctx->response, reason, sizeof(reason));
}

Управляющие кадры — низкоуровневый API

Обычно фреймворк сам отвечает на PING кадром PONG и согласует закрытие. websocketsresponse_pong / websocketsresponse_close нужны только для ручного управления жизненным циклом соединения.

Сжатие (permessage-deflate)

Если соединение согласовало расширение permessage-deflate (RFC 7692), фреймворк автоматически сжимает исходящие текстовые и бинарные кадры размером от 128 байт. В сжатом кадре устанавливается бит RSV1, а длина полезной нагрузки кодируется как обычно.

Сжатие включается только когда оно действительно уменьшает размер; иначе кадр уходит несжатым. Если сжатие завершается ошибкой, фреймворк также откатывается к несжатой отправке — обработчику ничего проверять не нужно.

Настройка согласования расширения описана в конфигурации WebSocket-маршрутов (config.json).

Важно

  • Клиенту ничего не отправляется, пока не вызван один из send_* (send_text, send_textn, send_binary, send_binaryn, send_data, send_datan, send_file, send_filen) или управляющая функция (websocketsresponse_pong / websocketsresponse_close).
  • После отправки ответа завершайте обработчик через return — повторная отправка в одном запросе сформирует некорректный кадр.
  • send_binary вычисляет длину через strlen() — для бинарных данных со встроенными нулями используйте send_binaryn.
  • send_file/send_filen отправляют файл относительно корня сервера и сами формируют кадр-сообщение при ошибке доступа, дополнительная отправка не требуется.
  • Получение данных от клиента описано в разделе Получение данных от клиента; широковещательная рассылка подписчикам — в Широковещание.

Выпущено под лицензией MIT.