SSE客户端C++实现(使用libcurl)

[复制链接]
发表于 2025-7-21 15:12:54 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
  1、SSE数据包格式
  如下所示,一条SSE消息中可以有一个或多个message,每个message由\n\n分隔,一个message也可以由一个或多个filed组成,每个filed由\n分隔,filed有data、id、event、retry四种。
  data表示消息数据,如"data:value\n"。
  id表示数据包编号,当连接断开重连的时间,客户端应该发送一个 HTTP 头,里面包含一个特殊的Last-Event-ID头信息,值为收到的末了一条数据的id。
  event表示自定义的事件类型。
  retry表示浏览器重新发起连接的隔断时间,当时间隔断到期,客户端应该重新发起连接。
  还可以有仅以冒号开头的行,表示解释。通常服务器每隔一段时间就会向浏览器发送一个解释,保持连接不中断。
  包含一条message的消息:
  1. data: {"username": "bobby", "time": "02:33:48"}
复制代码
  包含两条message的消息:
  1. data:this is message A
  2. data:this is
  3. data:message B
复制代码
  包含三条message的消息:
  1. :explan textdata:this is message A
  2. data:this is
  3. data:message B
复制代码
2、SSE客户端实现
  1. #include <thread>
  2. #include <functional>
  3. size_t dataCallback(void* ptr, size_t size, size_t nmemb, void* param)
  4. {
  5.     if (ptr == NULL || size == 0 || param == NULL)
  6.         return 0;
  7.     int s = size * nmemb;
  8.     std::string strMsg((char*)ptr, s);
  9.     CSSEClient* client = static_cast<CSSEClient*>(param);
  10.     if (client && client->onMessage)
  11.         client->onMessage(strMsg);
  12.     return s;
  13. }
  14. int progressCallback(void* param, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
  15.     CSSEClient* client = static_cast<CSSEClient*>(param);
  16.     return client->isExit() ? 1 : 0;  // 返回非0值会中断curl_easy_perform()
  17. }
  18. class CSSEClient
  19. {
  20. public:
  21.     CSSEClient(const std::string& strURL) {
  22.         curl_global_init(CURL_GLOBAL_DEFAULT);
  23.         _strURL = strURL;
  24.         _thread = std::thread(&CSSEClient::run, this);
  25.     }
  26.     virtual ~CSSEClient(){
  27.         _bExit = true;
  28.         _thread.join();
  29.         curl_global_cleanup();
  30.     }
  31.     bool isExit() { return _bExit; }
  32.     std::function<void(const std::string&)> onMessage;
  33. private:
  34.     void run() {
  35.         while (!_bExit) {
  36.             subscribeSSE();
  37.             if (_bExit)
  38.                 break;
  39.             std::this_thread::sleep_for(std::chrono::seconds(1));
  40.         }
  41.     }
  42.     void subscribeSSE() {
  43.         CURL* curl = curl_easy_init();
  44.         if (!curl) return;
  45.         struct curl_slist* headers = NULL;
  46.         headers = curl_slist_append(headers, "Accept: text/event-stream");
  47.         headers = curl_slist_append(headers, "Cache-Control: no-cache");
  48.         curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
  49.         curl_easy_setopt(curl, CURLOPT_URL, _strURL.c_str());
  50.         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dataCallback);
  51.         curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
  52.         curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0L);
  53.         curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); //开启TCP心跳
  54.         curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 60L); //空闲60秒后探测
  55.         curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 10L); //探测间隔10秒
  56.         curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);  // 开启传输进度(已下载/上传数据量、总数据量等)回调
  57.         curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progressCallback); //设置传输进度回调方法,即使没有数据传输该方法也会被调用(通常每秒1次),所以可以在该方法中判断是否需要退出
  58.         curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this); //设置传输回调方法的第一个参数
  59.         CURLcode res = curl_easy_perform(curl);
  60.         if (res == CURLE_ABORTED_BY_CALLBACK) {
  61.            //SSE connection aborted by user;
  62.         }
  63.         else {
  64.             //error
  65.         }
  66.         curl_easy_cleanup(curl);
  67.     }
  68.     std::thread _thread;
  69.     std::string _strURL;
  70.     std::atomic_bool _bExit = false;
  71. };
复制代码
  比如,客户端订阅用户相干数据的变化:
  1. CSSEClient _sseClient("http://test-api.baidu.com.cn:8080/SSE/subscribe/userID")
复制代码
  对于需要认证的SSE服务,需添加CURLOPT_USERPWD或OAuth头。
  
 
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表