当前位置: 首页 > news >正文

HTTP SSE 实现

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

一句概扩

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

下面是基于libhv实现的SSE

SSE server
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
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
 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);
        });

在这里插入图片描述

相关文章:

  • RabbitMq 基础
  • 贪心算法
  • 前端面试真题 2025最新版
  • ecovadis社会企业责任认证
  • C++面试笔记(持续更新...)
  • Transformer解析——(四)Decoder
  • Modbus协议基础
  • AWS云从业者认证题库 AWS Cloud Practitioner(2.21)
  • 【练习】【回溯:组合:一个集合 元素可重复】力扣 39. 组合总和
  • 如何实现使用DeepSeek的CV模型对管道内模糊、低光照或水渍干扰的图像进行去噪、超分辨率重建。...
  • 推理模型时代:大语言模型如何从对话走向深度思考?
  • java后端开发day18--学生管理系统
  • 多门店协同管理困难重重,管理系统如何破局?
  • MySQL 中的回表是什么?MySQL 中使用索引一定有效吗?如何排查索引效果?在 MySQL 中建索引时需要注意哪些事项?
  • matlab 轮边驱动系统汽车垂向动力学分析
  • NVM是什么,以及NVM的作用?
  • 代码讲解系列-CV(六)——视觉生成模型
  • Unity学习笔记-Unity了解,安装,简单配置(一)
  • Trae AI驱动开发实战:30分钟从0到1实现Django REST天气服务
  • 论文解读 | AAAI'25 Cobra:多模态扩展的大型语言模型,以实现高效推理
  • 白云鄂博矿区网站建设/专业关键词排名软件
  • 济南企业网站搭建/企业建站用什么好
  • 常州建设网站平台/磁力宅
  • 网站开发建设合同/百度竞价怎么排名第一
  • 网站建设制作浩森宇特/自动连点器
  • 做外贸纱线用什么网站/微信朋友圈推广文案