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

  • 21
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: mongoose是一个基于C语言编写的高性能的多线程HTTP服务器。它可以通过设置多个监听端口来实现多端口的HTTP服务器。 在mongoose中,可以通过调用`mg_bind()`函数指定要监听的多个端口。这个函数接受一个指向`struct mg_connection`的指针,该结构体包含了服务器相关的信息,如IP地址、端口号等。通过对这个结构体进行配置,可以实现多端口监听。 具体实现步骤如下: 1. 创建一个`struct mg_mgr`结构体对象,用于管理Mongoose实例和连接。 2. 调用`mg_mgr_init()`函数初始化`struct mg_mgr`对象。 3. 调用`mg_bind()`函数,传入要监听的端口号,与之前创建的`struct mg_connection`对象关联。 4. 调用`mg_set_protocol_http_websocket()`函数,将协议设置为HTTP。 5. 调用`mg_mgr_poll()`函数,开始监听端口并处理请求。 下面是一个简单的示例代码: ```c #include "mongoose.h" int main() { struct mg_mgr mgr; struct mg_connection *conn; mg_mgr_init(&mgr); // 创建一个结构体,用于保存服务器相关的信息 conn = mg_bind(&mgr, "8080", NULL); // 添加其他需要监听的端口 mg_bind(&mgr, "8081", NULL); mg_bind(&mgr, "8082", NULL); mg_set_protocol_http_websocket(conn); for (;;) { mg_mgr_poll(&mgr, 1000); } mg_mgr_free(&mgr); return 0; } ``` 通过上述代码,我们就可以在8080、8081和8082等多个端口上启动监听HTTP请求的服务器。 总结来说,mongoose可以通过设置多个监听端口来实现多端口HTTP服务器。通过创建多个`struct mg_connection`对象,并绑定到不同的端口,就可以实现在多个端口上同时监听HTTP请求。 ### 回答2: Mongoose是一个轻量级的嵌入式Web服务器软件,它可以实现多端口的HTTP服务器,具有简单易用、灵活可配置的特点。 要实现多端口的HTTP服务器,我们需要先创建一个Mongoose实例,然后配置不同端口的监听。 首先,我们创建Mongoose实例: ```cpp mg_mgr_init(&mgr, NULL); ``` 然后,我们按照需要配置不同端口的监听。假设我们要监听两个端口,分别为8000和9000,我们可以这样配置: ```cpp struct mg_connection *conn1 = mg_bind(&mgr, "8000", ev_handler); struct mg_connection *conn2 = mg_bind(&mgr, "9000", ev_handler); ``` 其中,ev_handler是处理事件的函数。 接下来,我们需要进入一个循环,以便Mongoose能够监听并处理客户端请求: ```cpp while (true) { mg_mgr_poll(&mgr, 1000); } ``` 在处理事件的函数中,我们可以根据具体的需求进行处理。例如,当有客户端连接时,我们可以向客户端发送欢迎信息: ```cpp static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { if (ev == MG_EV_ACCEPT) { mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, World!"); nc->flags |= MG_F_SEND_AND_CLOSE; } } ``` 通过以上步骤,我们就可以实现多端口的HTTP服务器了。我们可以根据实际需求配置不同的端口和处理逻辑,以满足多样化的应用场景。同样的,我们也可以使用其他编程语言来使用Mongoose实现多端口的HTTP服务器。 ### 回答3: Mongoose是一个嵌入式Web服务器,它可以帮助我们实现多端口的HTTP服务器Mongoose具有非常灵活的配置选项,可以轻松地配置多个端口。 要实现多端口的HTTP服务器,我们需要首先在代码中导入Mongoose库,并创建一个Mongoose实例。然后,我们可以使用`mg_bind()`函数将多个端口绑定到Mongoose实例上。 例如,下面的代码片段展示了如何使用Mongoose实现多端口的HTTP服务器: ```c #include "mongoose.h" int main() { struct mg_mgr mgr; struct mg_connection *nc; const char *ports[] = {"8080", "8888", NULL}; // 定义需要监听的端口 mg_mgr_init(&mgr, NULL); // 循环遍历端口列表,绑定每个端口到Mongoose实例 for (int i = 0; ports[i] != NULL; i++) { nc = mg_bind(&mgr, ports[i], ev_handler); if (nc == NULL) { printf("Failed to bind to port %s\n", ports[i]); continue; } mg_set_protocol_http_websocket(nc); } printf("Mongoose HTTP server started on ports %s\n", ports); while (1) { mg_mgr_poll(&mgr, 1000); } mg_mgr_free(&mgr); return 0; } // 处理请求的回调函数 void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { // 在这里处理HTTP请求 } ``` 上述代码中,我们首先定义了需要监听的端口列表,包括8080和8888两个端口。然后,通过循环遍历端口列表,在Mongoose实例中绑定每个端口。如果绑定失败,则会打印错误信息。接着,设置协议为HTTP,并进入循环以等待来自客户端的请求。 在回调函数`ev_handler`中,我们可以根据需要来处理HTTP请求。可以处理GET、POST等不同类型的请求,并根据请求内容返回相应的响应。 通过使用Mongoose,我们可以轻松实现多端口的HTTP服务器。无论是需要同时监听多个端口,还是在不同的端口上提供不同的服务,Mongoose都是一个非常便捷和可靠的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值