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);
        });
相关推荐
楷哥爱开发11 小时前
HTTP代理是什么?性能分析、适用场景与配置教程
网络·网络协议·http
IPdodo_11 小时前
2026年AI 数据采集代理 IP 选型:成功率、并发、轮换与成本评估
前端·网络·人工智能·chrome·python·http·网络调试
鱼很腾apoc11 小时前
【Linux】第13期 详解应用层协议HTTP+Cookie/Session+HTTPS
linux·服务器·c++·学习·http·https
q5673152312 小时前
Curl 报 CONNECT tunnel failed, response 6xx:排查思路全解
数据库·网络协议·scrapy·http·中间件·http代理
瓦学妹13 小时前
HTTP代理性能分析:如何利用 IPFoxy 快速灵活切换协议?
网络·网络协议·http
2601_9567436813 小时前
上海物联网应用开发技术选型指南:从HTTP/MQTT/Modbus协议适配、平台化架构设计到源代码交付与私有化部署,解析工程落地的关键考量点
物联网·网络协议·http·开发经验·上海
Shadow(⊙o⊙)14 小时前
进程组、会话、守护进程
网络·网络协议·http
++==1 天前
RESTful详解:核心思想、框架、与HTTP的区别,API的设计风格
后端·http·restful
huashengzsj1 天前
企业OV泛域名证书价格多少?2026年市场报价与选购指南
网络协议·http·https
GitLqr1 天前
从 TCP 底层看实时通信:为什么 SSE 往往比 WebSocket 更香?
websocket·http·openai