mongoose实现Server-Sent Events服务器

这里写自定义目录标题

Server-Sent Events

服务器向浏览器推送信息,除了 WebSocket,还有一种方法:Server-Sent Events(以下简称 SSE)。
严格地说,HTTP 协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming)。

也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。

SSE 就是利用这种机制,使用流信息向浏览器推送信息。它基于 HTTP 协议,目前除了 IE/Edge,其他浏览器都支持。

Mongoose

Mongoose is a networking library for C/C++. It implements event-driven, non-blocking APIs for TCP, UDP, HTTP, WebSocket, MQTT. It connects devices and brings them online. Since 2004, a number of open source and commercial products have utilized it. It even runs on the International Space Station! Mongoose makes embedded network programming fast, robust, and easy.

Mongoose works on Windows, Linux, Mac, and on a many embedded architectures such as STM32, NXP, TI, ESP32, and so on. It can run on top of the existing OS and TCP/IP stack like FreeRTOS and lwIP, as well as on a bare metal, utilising Mongoose’s built-in TCP/IP stack and network drivers.

代码实现

计时器事件处理程序获取一个指向事件管理器的指针作为其参数,我们需要它遍历所有连接,并在初始化部分进行配置:

// An highlighted block
static const char *s_listening_address = "http://0.0.0.0:8000";
static const char *s_root_dir = "web_root";

int main(void)
{
  struct mg_mgr mgr;

  mg_mgr_init(&mgr);
  mg_http_listen(&mgr, s_listening_address, cb, NULL);
  mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, timer_callback, &mgr);
  
  // Start infinite event loop
  MG_INFO(("Mongoose version : v%s", MG_VERSION));
  MG_INFO(("Listening on     : %s", s_listening_address));
  MG_INFO(("Web root         : [%s]", s_root_dir));

  for (;;)
    mg_mgr_poll(&mgr, 50);
  mg_mgr_free(&mgr);

  return 0;
}

当我们收到 URI 的 HTTP 请求时,我们标记该连接并提供一个标头,指示浏览器避免缓存该内容,并指示我们的响应内容类型将是 ,然后我们设置一个花哨的边界标记,用于分隔帧

#include "mongoose.h"

// HTTP request handler function. It implements the following endpoints:
//   /api/video1 - hangs forever, returns MJPEG video stream
//   all other URI - serves web_root/ directory
static void cb(struct mg_connection *c, int ev, void *ev_data)
{
  if (ev == MG_EV_HTTP_MSG)
  {
    struct mg_http_message *hm = (struct mg_http_message *)ev_data;
    if (mg_match(hm->uri, mg_str("/api/video1"), NULL))
    {
      c->data[0] = 'V'; // Mark that connection as video live streamer
      mg_printf(
          c, "%s",
          "HTTP/1.0 200 OK\r\n"
          "Cache-Control: no-cache\r\n"
          "Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
          "Content-Type: multipart/x-mixed-replace; boundary=--foo\r\n\r\n");
    }
    else 
    if (mg_match(hm->uri, mg_str("/sse"), NULL))
    {
      c->data[0] = 'E'; // Mark that connection as event live streamer
      mg_printf(
          c, "%s",
          "HTTP/1.0 200 OK\r\n"
          "Cache-Control: no-cache\r\n"
          "Transfer-Encoding: chunked\r\n"
          "Access-Control-Allow-Origin: *\r\n"
          "Content-Type: text/event-stream; boundary=--foo\r\n\r\n");
    }
    else
    {
      struct mg_http_serve_opts opts = {.root_dir = "web_root"};
      mg_http_serve_dir(c, ev_data, &opts);
    }
  }
}

然后,我们定期遍历所有标记的连接,并发送边界标记和新图像

static void broadcast_events(struct mg_mgr *mgr)
{
  const char *files[] = {"images/1.jpg", "images/2.jpg", "images/3.jpg",
                         "images/4.jpg", "images/5.jpg", "images/6.jpg"};
  size_t nfiles = sizeof(files) / sizeof(files[0]);
  static size_t i;
  const char *path = files[i++ % nfiles];
  struct mg_str data = mg_file_read(&mg_fs_posix, path); // Read next file
  struct mg_connection *c;
  for (c = mgr->conns; c != NULL; c = c->next)
  {
    if (c->data[0] != 'E')
      continue; // Skip non-stream connections
    if (data.buf == NULL)
      continue; // Skip on file read error
    mg_printf(c,
              "--foo\r\nContent-Type: image/jpeg\r\n"
              "Content-Length: %lu\r\n\r\n",
              data.len);
    mg_printf(c, "%s", "data: 1231");
    mg_send(c, data.buf, data.len);
    mg_send(c, "\r\n", 2);
  }
  free((void *)data.buf);
}

static void timer_callback(void *arg)
{
  broadcast_mjpeg_frame(arg);
  broadcast_events(arg);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值