STTNet 0.7.0

第 7 章:WebSocket

本章介绍 WebSocket 握手检查、回调类型、文本/二进制帧、心跳和关闭流程。

1. WebSocket 生命周期

阶段接口返回值
握手前检查setJudgeFunctionbool;false 拒绝连接。
握手成功初始化setStartFunctionbool;可发送欢迎消息。
普通消息处理setGlobalSolveFunctionbool;false 表示失败并关闭。
按 key 多阶段处理setGetKeyFunction + setFunctionint;返回值采用 1/0/-1/-2 流程语义。

2. 完整 Echo Demo

#include <sttnet.h>

#include <iostream>
#include <string_view>

int main()
{
    using namespace stt::network;
    using namespace stt::data;
    using stt::system::ServerSetting;

    if(!ServerSetting::blockTerminationSignals())
    {
        std::cerr << "failed to block termination signals\n";
        return 1;
    }

    WebSocketServer server;

    // 握手判定函数检查 HTTP Upgrade 请求。
    // 本 Demo 只接受 /echo 和可选查询参数。
    server.setJudgeFunction(
        [](WebSocketFDInformation &connection) {
            const bool correctPath =
                connection.locPara == "/echo" ||
                connection.locPara.rfind("/echo?", 0) == 0;

            // 真实服务还可以在这里检查 connection.header,例如
            // 使用 HttpStringUtil::get_value_header()。
            return correctPath;
        });

    // 全局回调接收未使用自定义 key 路由的消息。
    // 它返回 bool;false 表示消息处理失败。
    server.setGlobalSolveFunction(
        [](WebSocketServerFDHandler &client,
           WebSocketFDInformation &message) {
            // RFC 6455 中 opcode 1 是文本,opcode 2 是二进制。
            const std::string frameType =
                message.message_type == 2 ? "0010" : "0001";

            // 使用相同的文本/二进制帧类型回显负载。
            return client.sendMessage(message.message, frameType);
        });

    // 关闭空闲连接,并要求 30 秒内返回心跳响应。
    server.setTimeOutTime(20);   // 空闲超时,单位分钟。
    server.setHBTimeOutTime(30); // 心跳响应超时,单位秒。

    if(!server.startListen(5050))
    {
        std::cerr << "failed to listen on port 5050\n";
        return 2;
    }

    std::cout << "WebSocket echo listening on ws://127.0.0.1:5050/echo\n";
    ServerSetting::waitForTerminationSignal();
    return server.close() ? 0 : 3;
}

3. 握手 Header 校验

server.setJudgeFunction([](WebSocketFDInformation &info) {
    std::string_view token;
    stt::data::HttpStringUtil::get_value_header(
        std::string_view(info.header), token, "X-Demo-Token");
    return info.locPara == "/echo" && token == "sttnet";
});

生产环境通常会校验 path、Origin、认证 token 或 Cookie。拒绝时返回 false,框架不会继续握手。

4. 文本、二进制和控制帧

  • message_type == 1 通常是文本帧,发送类型使用 "0001"
  • message_type == 2 是二进制帧,发送类型使用 "0010"
  • Ping/Pong 属于协议控制帧,不应替代业务层“用户在线”心跳。
  • 协议级关闭由关闭帧完成,例如 server.closeFD(fd, 1000, "bye")

5. 测试

./build/examples/sttnet_websocket_echo

# 安装 websocat 后:
websocat ws://127.0.0.1:5050/echo
# 输入 hello,服务端应原样回显。

浏览器控制台也可以测试:

const ws = new WebSocket("ws://127.0.0.1:5050/echo");
ws.onmessage = e => console.log(e.data);
ws.onopen = () => ws.send("hello");