Unix网络编程——Mongoose开源项目源码解读(1):概述

参考资料:https://docs.cesanta.com/mongoose/master/
源码下载地址:https://github.com/cesanta/mongoose
当前最新版本号:Mongoose 6.7
声明:本文章参照Mongoose网站说明,可视为其简略翻译,只做学习记录使用,由于能力有限,不保证一定准确无误。如需转载请注明出处,谢谢~

简述:
Mongoose前身为shttpd,使用标准C/C++编写而成,转为嵌入式设备设计的,支持跨平台的网络服务器库。Mongoose实现了非阻塞式事件驱动API,支持TCP, UDP, HTTP, WebSocket, CoAP, MQTT。

Mongoose的三个基本结构体:

struct mg_mgr;           // 持有所有活动的连接的事件管理器
struct mg_connection ;   // 用于连接的套接字状态的描述
struct mbuf ;            // 用于接收和发送数据缓存的描述

声明与初始化事件管理器

struct mg_mgr mgr;
mg_mgr_init(&mgr, NULL);

创建连接,比如创建一个服务器端的监听套接字

struct mg_connection *c = mg_bind(&mgr, "80", ev_handler_function);
mg_set_protocol_http_websocket(c);

轮询处理:遍历所有套接字,接收新的连接请求,发送与接受数据,关闭连接,事件处理等

for (;;) {
   mg_mgr_poll(&mgr, 1000);
 }

收发缓冲区
每个连接都有自己的收发缓冲区struct mbuf,当Mongoose 接收到数据时数据被追加到接收缓冲区并触发一个MG_EV_RECV 事件。当需要发送数据时,只需要使用 mg_send()或者mg_printf()将数据追加到发送缓冲区,当数据发送成功,Mongoose 将数据从发送缓冲区删除并发送一个MG_EV_SEND事件。当连接关闭时,触发MG_EV_CLOSE事件。以下为mbuf的定义:

/* Memory buffer descriptor */
struct mbuf {
  char *buf;   /* Buffer pointer */
  size_t len;  /* Data length. Data is located between offset 0 and len. */
  size_t size; /* Buffer size allocated by realloc(1). Must be >= len */
};

事件及事件处理函数
Mongoose 为连接、读写、关闭等都定义了事件,每个连接都有与其关联的事件处理函数——该函数由用户自身实现,该函数的原型如下:

static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
  switch (ev) {
    /* Event handler code that defines behavior of the connection */
    ...
  }
}

典型的事件序列如下:

  • 对于客户端:MG_EV_CONNECT -> (MG_EV_RECV, MG_EV_SEND, MG_EV_POLL …) -> MG_EV_CLOSE
  • 对于服务端: MG_EV_ACCEPT -> (MG_EV_RECV, MG_EV_SEND, MG_EV_POLL …) -> MG_EV_CLOSE

以下为Mongoose的核心事件:

  • MG_EV_ACCEPT:当accept到一个新的连接请求时触发该事件,ev_data 为socket_address 联合体
  • MG_EV_CONNECT:当使用mg_connect() 创建连接套接字时触发该事件,ev_data 为int *success,success为0表示成功,否则为失败的errno
  • MG_EV_RECV:接收到新的数据,void *ev_data 是接收到的字节数,接收到应该使用recv_mbuf来获取数据,使用mg_send()发送数据。Mongoose 使用realloc来扩展接收缓冲区,但是需要由用户删除已接收的数据——比如mbuf_remove()。
  • MG_EV_SEND:Mongoose 已经将(int *)ev_data 的数据写到了远端并将数据从send_mbuf中删除。mg_send()并不发送数据,只是将数据追加到缓冲区,正真的IO操作由mg_mgr_poll()完成。
  • MG_EV_POLL:该事件被发送给所有的已连接套接字,它可以被用来作任何持续性的事务,比如检查某个连接是否已经超时或者关闭、过期,或者用来发送心跳消息。
  • MG_EV_TIMER:当mg_set_timer() 被调用时被用来发送给指定的connection

连接标记位
每个连接都有自己的标记位,比如当创建一个UDP连接时,Mongoose 将为这个连接的标记为设置为MG_F_UDP。

以下标记为用户设定:
- MG_F_FINISHED_SENDING_DATA:告诉Mongoose 数据已经全部存放到了发送缓冲区,当Mongoose 将数据发送完毕时,主动关闭连接。
- MG_F_BUFFER_BUT_DONT_SEND :告诉Mongoose 只将数据追加到缓冲区但是不要发送数据,因为数据之后可能会被修改,当MG_F_BUFFER_BUT_DONT_SEND标记位被清除时数据再被发送
- MG_F_CLOSE_IMMEDIATELY :告诉Mongoose 立马关闭连接,一般在出错的时候设置
- MG_F_USER_1, MG_F_USER_2, MG_F_USER_3, MG_F_USER_4:用户自定义,用来存放应用的指定状态

以下标记由Mongoose 设定:

  • MG_F_SSL_HANDSHAKE_DONE SSL:只有在SSL连接中才会设置,当SSL的握手完成时设定
  • MG_F_CONNECTING:在调用mg_connect() 后但是连接还没有完成时设置
  • MG_F_LISTENING:为所有监听套接字设置
  • MG_F_UDP:如果是UDP协议则设置
  • MG_F_IS_WEBSOCKET:如果是网络套接字则设置
  • MG_F_WEBSOCKET_NO_DEFRAG:由用户希望关闭自动的websocket框架碎片整理时设置

编译选项:
Mongoose 源代码由单一的c文件构成,Mongoose 所支持的协议都由它提供。在编译时Mongoose 可以去除不需要的功能以减小执行文件的大小。比如可以使用-D MG_DISABLE_MQTT -D MG_DISABLE_COAP去除代码中的MQTT和CoAP的支持代码。
linux下的编译样例:

cc my_app.c mongoose.c -D MG_DISABLE_MQTT -D MG_DISABLE_COAP

Mongoose 的使用样例:
1.将mongoose.c 和mongoose.h拷贝到你的工程目录下
2.使用mongoose提供的API编写工程,例如my_app.c
3.编译工程:cc my_app.c mongoose.c

#include "mongoose.h"  // Include Mongoose API definitions

// Define an event handler function
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
  struct mbuf *io = &nc->recv_mbuf;

  switch (ev) {
    case MG_EV_RECV:
      // This event handler implements simple TCP echo server
      mg_send(nc, io->buf, io->len);  // Echo received data back
      mbuf_remove(io, io->len);      // Discard data from recv buffer
      break;
    default:
      break;
  }
}

int main(void) {
  struct mg_mgr mgr;

  mg_mgr_init(&mgr, NULL);  // Initialize event manager object

  // Note that many connections can be added to a single event manager
  // Connections can be created at any point, e.g. in event handler function
  mg_bind(&mgr, "1234", ev_handler);  // Create listening connection and add it to the event manager

  for (;;) {  // Start infinite event loop
    mg_mgr_poll(&mgr, 1000);
  }

  mg_mgr_free(&mgr);
  return 0;
}

你可能感兴趣的:(【Linux网络编程学习】)