HTTP SSE 实现

参考:
SSE协议
SSE技术详解:使用 HTTP 做服务端数据推送应用的技术

一句概扩

SSE可理解为:服务端和客户端建立连接之后双方均保持连接,但仅支持服务端向客户端推送数据。推送完毕之后关闭连接,无状态行。

下面是基于libhv实现的SSE

SSE server
cpp 复制代码
int Handler::sse(const HttpContextPtr& ctx) {
    // SSEvent(message) every 1s
    hv::setInterval(10000, [ctx](hv::TimerID timerID) {
        static int ncount = 0;
        if (ctx->writer->isConnected()) {
            char szTime[DATETIME_FMT_BUFLEN] = {0};
            datetime_t now = datetime_now();
            datetime_fmt(&now, szTime);
            ctx->writer->SSEvent(szTime);
            if (++ncount >= 10) {
                //hv::killTimer(timerID);
                ctx->writer->close();
                ncount = 0;
            }
        } else {
            hv::killTimer(timerID);
        }
    });
    return HTTP_STATUS_UNFINISHED;
}
SSE client
cpp 复制代码
typedef std::function<void(const std::string& sid, const std::string& sevent, const std::string& sdata, const unsigned int retry_ms)> sse_msg_cb;
HV_INLINE int sse(http_method method, const char* url, const sse_msg_cb& msg_cb, const http_body& body = NoBody, const http_headers& headers = DefaultHeaders,const unsigned int timeout_s = -1) {
    hv::HttpClient cli;
    HttpRequest req;
    HttpResponse resp;

    req.url = url; //
    req.method = method;
    req.timeout = timeout_s; // 不超时
    if (&body != &NoBody) {
        req.body = body;
    }
    if (&headers != &DefaultHeaders) {
        req.headers = headers;
    }

    bool bstream = false;
    req.http_cb = [msg_cb, &bstream](HttpMessage* resp, http_parser_state state, const char* data, size_t size) {
        if (state == HP_HEADERS_COMPLETE) {
            if (resp->headers["Content-Type"] == "text/event-stream") {
                bstream = true;
                return 0;
            }
        }
        else if (state == HP_BODY) {
            /*binary body should check data*/
            // printf("%s", std::string(data, size).c_str());
            resp->body.append(data, size);
            if (!bstream) return 0;
            /*/n/n获取message*/
            size_t ifind = std::string::npos;
            while ((ifind = resp->body.find("\n\n")) != std::string::npos) {
                std::string msg = resp->body.substr(0, ifind + 2);
                resp->body.erase(0, ifind + 2);

                /*解析body,暂时不考虑多data
                id:xxx\n
                event:xxx\n
                data:xxx\n
                data:xxx\n
                data:xxx\n
                retry:10000\n
                */
                auto kvs = hv::splitKV(msg, '\n', ':');
                if (msg_cb && (kvs.count("id") || kvs.count("event") || kvs.count("data") || kvs.count("retry")))
                    msg_cb(kvs.count("id") ? kvs["id"] : "", kvs.count("event") ? kvs["event"] : "", kvs.count("data") ? kvs["data"] : "",
                           kvs.count("retry") ? atoi(kvs["retry"].c_str()) : 0);
            }
        }
        return 0;
    };
    return cli.send(&req, &resp);
}
测试Demo
cpp 复制代码
 sse(HTTP_GET,"http://127.0.0.1:12900/sse", [](const std::string& sid, const std::string& sevent,
  const std::string& sdata, const unsigned int retry_ms) { 
            printf("id:%s\r\nevent:%s\r\ndata:%s\r\nretry:%u\r\n\r\n",
            sid.c_str(),sevent.c_str(),sdata.c_str(),retry_ms);
        });
相关推荐
2501_915921434 小时前
iOS IPA 混淆实测分析:从逆向视角验证加固效果与防护流程
websocket·网络协议·tcp/ip·http·网络安全·https·udp
2501_915918414 小时前
打造可观测的 iOS CICD 流程:调试、追踪与质量保障全记录
websocket·网络协议·tcp/ip·http·网络安全·https·udp
小菜鸡95277 小时前
http、SSL、TLS、https、证书
http·https·证书·ssl·tls
2501_915909068 小时前
调试 WebView 旧资源缓存问题:一次从偶发到复现的实战经历
websocket·网络协议·tcp/ip·http·网络安全·https·udp
小湘西8 小时前
Apache HttpClient 的请求模型和 I/O 类型
java·http·apache
cliffordl9 小时前
MCP 传输机制(Streamable HTTP)
网络·网络协议·http
2501_9159214310 小时前
请求未达服务端?iOS端HTTPS链路异常的多工具抓包排查记录
websocket·网络协议·tcp/ip·http·网络安全·https·udp
妮妮喔妮11 小时前
HTTP中常见的Content-Type
网络·网络协议·http
危险、12 小时前
RabbitMQ 通过HTTP API删除队列命令
分布式·http·rabbitmq
超人不会飛12 小时前
就着HTTP聊聊SSE的前世今生
前端·javascript·http