Files
cpp-httplib/docs-src/pages/en/cookbook/w04-websocket-binary.md
T
yhirose 9bfd2ddf1c Fix README and docs pages that no longer match the code
Bring the READMEs and the docs pages (en and ja) in line with the
current header:

- Fix the client sample that does not compile, the Transfer-Encoding
  statement for default headers, the verifier callback comment, the
  thread pool spawn rule, the sanitize_filename description, and the
  inverted Accept-Encoding sentence.
- Describe recent behaviour: TLS errors behind Read/Write failures,
  Host validation, Connection: close for a body without length,
  content_reader rules, refused CONNECT, WebSocketClient proxies,
  CPPHTTPLIB_INTERIM_RESPONSE_MAX_COUNT, the SSE event size limit, and
  the payload limit of the Stream API.
- Complete the API references of WebSocket, WebSocketClient and the
  stream:: helpers, and the SSE reconnect rules.
- Fix cookbook pages: keychain loading on every backend, received Close
  status, a multipart sample that trusted the client's file name, an
  echo sample that sent binary as text, SNI, pre-request handler scope,
  stop() with WebSocket handlers, and the thread pool notes.
- Restore the mutex in the LLM app's Chapter 5 code.
2026-10-18 10:13:57 -04:00

3.0 KiB

title, order, status
title order status
W04. Send and Receive Binary Frames 55 draft

WebSocket has two frame types: text and binary. JSON and plain text go in text frames; images and raw protocol bytes go in binary. In cpp-httplib, send() picks the right type via overload.

How to pick a frame type

ws.send(std::string("Hello"));           // text
ws.send("Hello", 5);                      // binary
ws.send(binary_data, binary_data_size);   // binary

The std::string overload sends as text. The const char* + size overload sends as binary. A bit subtle, but once you know it, it sticks.

If you have a std::string and want to send it as binary, pass .data() and .size() explicitly.

std::string raw = build_binary_payload();
ws.send(raw.data(), raw.size()); // binary frame

Detect frame type on receive

The return value of ws.read() tells you whether the received frame was text or binary.

std::string msg;
auto result = ws.read(msg);

switch (result) {
  case httplib::ws::ReadResult::Text:
    std::cout << "text: " << msg << std::endl;
    break;
  case httplib::ws::ReadResult::Binary:
    std::cout << "binary: " << msg.size() << " bytes" << std::endl;
    handle_binary(msg.data(), msg.size());
    break;
  case httplib::ws::ReadResult::Fail:
    // error or closed
    break;
  case httplib::ws::ReadResult::Timeout:
    // read timeout elapsed; the connection is still open
    break;
}

Binary frames still come back in a std::string, but treat its contents as raw bytes — use msg.data() and msg.size().

When binary is the right call

  • Images, video, audio: No Base64 overhead
  • Custom protocols: protobuf, MessagePack, or any structured binary format
  • Game networking: When latency matters
  • Sensor data streams: Push numeric arrays directly

Ping is binary-ish, but hidden

WebSocket Ping/Pong frames are close cousins of binary frames at the opcode level, but cpp-httplib handles them automatically — you don't touch them. See W02. Set a WebSocket heartbeat.

Example: send an image

// Server: push an image
svr.WebSocket("/image", [](const auto &req, auto &ws) {
  auto img = read_image_file("logo.png");
  ws.send(img.data(), img.size());
});
// Client: receive and save
httplib::ws::WebSocketClient cli("ws://localhost:8080/image");
cli.connect();

std::string buf;
if (cli.read(buf) == httplib::ws::ReadResult::Binary) {
  std::ofstream ofs("received.png", std::ios::binary);
  ofs.write(buf.data(), buf.size());
}

You can mix text and binary in the same connection. A common pattern: JSON for control messages, binary for the actual data — you get efficient handling of metadata and payload both.

Note: An incoming message can be at most CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH (16MB by default, 0 for no limit). A larger one makes read() return Fail and ends the connection. Within that limit, a message is held in memory whole, so for very large data, chunk it in your application code.