# WebSocket

cpp-httplibはWebSocketにも対応しています。HTTPのリクエスト/レスポンスと違い、WebSocketはサーバーとクライアントが双方向にメッセージをやり取りできます。チャットやリアルタイム通知に便利です。

さっそく、エコーサーバーとクライアントを作ってみましょう。

## エコーサーバー

受け取ったメッセージをそのまま返すエコーサーバーです。

    #include "httplib.h"
    #include <iostream>

    int main() {
        httplib::Server svr;

        svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
            std::string msg;
            while (ws.read(msg)) {
                ws.send(msg);  // 受け取ったメッセージをそのまま返す
            }
        });

        std::cout << "Listening on port 8080..." << std::endl;
        svr.listen("0.0.0.0", 8080);
    }

    #include "httplib.h"
    #include <iostream>

    int main() {
        httplib::Server svr;

        svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
            std::string msg;
            while (ws.read(msg)) {
                ws.send(msg);  // 受け取ったメッセージをそのまま返す
            }
        });

        std::cout << "Listening on port 8080..." << std::endl;
        svr.listen("0.0.0.0", 8080);
    }

`svr.WebSocket()` でWebSocketハンドラーを登録します。3章の `svr.Get()` や `svr.Post()` と同じ感覚ですね。

ハンドラーの中では、`ws.read(msg)` でメッセージを待ちます。接続が閉じられると `read()` が `false` を返すので、ループを抜けます。`ws.send(msg)` でメッセージを送り返します。

## クライアントからの接続

`httplib::ws::WebSocketClient` を使ってサーバーに接続してみましょう。

    #include "httplib.h"
    #include <iostream>

    int main() {
        httplib::ws::WebSocketClient client("ws://localhost:8080/ws");

        if (!client.connect()) {
            std::cout << "Connection failed" << std::endl;
            return 1;
        }

        // メッセージを送信
        client.send("Hello, WebSocket!");

        // サーバーからの応答を受信
        std::string msg;
        if (client.read(msg)) {
            std::cout << msg << std::endl;  // Hello, WebSocket!
        }

        client.close();
    }

    #include "httplib.h"
    #include <iostream>

    int main() {
        httplib::ws::WebSocketClient client("ws://localhost:8080/ws");

        if (!client.connect()) {
            std::cout << "Connection failed" << std::endl;
            return 1;
        }

        // メッセージを送信
        client.send("Hello, WebSocket!");

        // サーバーからの応答を受信
        std::string msg;
        if (client.read(msg)) {
            std::cout << msg << std::endl;  // Hello, WebSocket!
        }

        client.close();
    }

コンストラクタには `ws://host:port/path` 形式のURLを渡します。`connect()` で接続を開始し、`send()` と `read()` でメッセージをやり取りします。

## テキストとバイナリ

WebSocketにはテキストとバイナリの2種類のメッセージがあります。`read()` の戻り値で区別できます。

    svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
        std::string msg;
        httplib::ws::ReadResult ret;
        while ((ret = ws.read(msg))) {
            if (ret == httplib::ws::Binary) {
                ws.send(msg.data(), msg.size());  // バイナリとして送信
            } else {
                ws.send(msg);  // テキストとして送信
            }
        }
    });

    svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
        std::string msg;
        httplib::ws::ReadResult ret;
        while ((ret = ws.read(msg))) {
            if (ret == httplib::ws::Binary) {
                ws.send(msg.data(), msg.size());  // バイナリとして送信
            } else {
                ws.send(msg);  // テキストとして送信
            }
        }
    });

- `ws.send(const std::string &)` — テキストメッセージとして送信
- `ws.send(const char *, size_t)` — バイナリメッセージとして送信

クライアント側も同じAPIです。

## リクエスト情報へのアクセス

ハンドラーの第1引数 `req` から、ハンドシェイク時のHTTPリクエスト情報を読み取れます。認証トークンの確認などに便利です。

    svr.WebSocket("/ws", [](const httplib::Request &req, httplib::ws::WebSocket &ws) {
        auto token = req.get_header_value("Authorization");
        if (token.empty()) {
            ws.close(httplib::ws::CloseStatus::PolicyViolation, "unauthorized");
            return;
        }

        std::string msg;
        while (ws.read(msg)) {
            ws.send(msg);
        }
    });

    svr.WebSocket("/ws", [](const httplib::Request &req, httplib::ws::WebSocket &ws) {
        auto token = req.get_header_value("Authorization");
        if (token.empty()) {
            ws.close(httplib::ws::CloseStatus::PolicyViolation, "unauthorized");
            return;
        }

        std::string msg;
        while (ws.read(msg)) {
            ws.send(msg);
        }
    });

## WSSで使う

HTTPS上のWebSocket（WSS）にも対応しています。サーバー側は `httplib::SSLServer` にWebSocketハンドラーを登録するだけです。

    httplib::SSLServer svr("cert.pem", "key.pem");

    svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
        std::string msg;
        while (ws.read(msg)) {
            ws.send(msg);
        }
    });

    svr.listen("0.0.0.0", 8443);

    httplib::SSLServer svr("cert.pem", "key.pem");

    svr.WebSocket("/ws", [](const httplib::Request &, httplib::ws::WebSocket &ws) {
        std::string msg;
        while (ws.read(msg)) {
            ws.send(msg);
        }
    });

    svr.listen("0.0.0.0", 8443);

クライアント側は `wss://` スキームを使います。

    httplib::ws::WebSocketClient client("wss://localhost:8443/ws");

    httplib::ws::WebSocketClient client("wss://localhost:8443/ws");

## 次のステップ

WebSocketの基本がわかりましたね。ここまでで Tourは終わりです。

次のページでは、Tourで取り上げなかった機能をまとめて紹介します。

次: What's Next