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);
        });
相关推荐
Hello.Reader4 小时前
Spring 新声明式 HTTP 客户端:HTTP Interface + RestClient,把“调用外部 API”写成接口
java·spring·http
IP搭子来一个4 小时前
什么是http代理,http代理的作用是什么?
网络·网络协议·http
飞剑神4 小时前
libhv HttpServer 路由函数定义
libhv
wb043072015 小时前
TCP/IP(IP、TCP、UDP、ICMP)与 HTTP/HTTPS
tcp/ip·http·udp
极安代理17 小时前
HTTP代理是什么?作用与场景全面解析
网络·网络协议·http
未来之窗软件服务19 小时前
幽冥大陆(一百07)—门禁局域网http获取名单—东方仙盟练气期
网络·http·仙盟创梦ide·东方仙盟·东方仙盟智能硬件·智能闸机
ps酷教程20 小时前
netty模拟文件列表http服务器
http·netty
人道领域1 天前
JavaWeb从入门到进阶(HTTP协议的请求与响应)
网络·网络协议·http
极安代理1 天前
HTTP代理IP如何提升爬虫采集效率?
爬虫·tcp/ip·http
草根站起来1 天前
https加密证书
网络协议·http·https