mongoose http 源码解析(1)

最近在看mongoose(版本6.13)的源码,作为网络编程入门,以此记录学习笔记,特此分享,有错还望各位指正。

下面是官网上的一个入门例子:一个简单的TCP服务器,它接收客户端的消息并回传给客户端。

其中主函数分为以下几步:
1.创建一个连接管理器(mg_mgr)
2.新建一个监听连接(mg_connection),绑定端口和回调函数,加入到事件管理器中
3.进入循环,轮询所有连接,如果有事件发生,则进行处理

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

在阅读源码之前,首先要了解两个结构体:mg_mgr和mg_connection

  1. mg_mgr:连接管理器(又或者叫事件管理器?),我觉得它更像是连接管理器,每次循环都检查所有连接,找出活动的连接,然后依次处理。这里说的连接不仅是指客户端和服务器建立的连接,也包括监听连接。
struct mg_mgr {
  struct mg_connection *active_connections;//所有活动的连接
#if MG_ENABLE_HEXDUMP
  const char *hexdump_file; /* Debug hexdump file path */
#endif
#if MG_ENABLE_BROADCAST
  sock_t ctl[2]; /* Socketpair for mg_broadcast() */
#endif
  void *user_data; /* User data */
  int num_ifaces;//网络接口的数量
  int num_calls;
  struct mg_iface **ifaces; /* network interfaces */
  const char *nameserver;   /* DNS server to use */
};

下面两行

 struct mg_mgr mgr;

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

就是创建了一个新的连接管理器。
mg_mgr_init的源码也很简单,即对结构体内的数据做一些赋值操作,创建网络接口以及ssl初始化等。(一开始没搞懂ifaces是个啥,其实就是网络端口,Linux下 ifconfig -a指令就可以查看。。)

//初始化事件管理器外部接口
void mg_mgr_init(struct mg_mgr *m, void *user_data) {
  struct mg_mgr_init_opts opts;
  memset(&opts, 0, sizeof(opts));
  mg_mgr_init_opt(m, user_data, opts);
}
//初始化事件管理器
void mg_mgr_init_opt(struct mg_mgr *m, void *user_data,
                     struct mg_mgr_init_opts opts) {
  memset(m, 0, sizeof(*m));
#if MG_ENABLE_BROADCAST
  m->ctl[0] = m->ctl[1] = INVALID_SOCKET;
#endif
  m->user_data = user_data;

#ifdef _WIN32
  {
    WSADATA data;
    WSAStartup(MAKEWORD(2, 2), &data);
  }
#elif defined(__unix__)
  /* Ignore SIGPIPE signal, so if client cancels the request, it
   * won't kill the whole process. */
  signal(SIGPIPE, SIG_IGN);
#endif

  {
    int i;
    if (opts.num_ifaces == 0) {
      opts.num_ifaces = mg_num_ifaces;
      opts.ifaces = mg_ifaces;
    }
    if (opts.main_iface != NULL) {
      opts.ifaces[MG_MAIN_IFACE] = opts.main_iface;
    }
    m->num_ifaces = opts.num_ifaces;
    m->ifaces =
        (struct mg_iface **) MG_MALLOC(sizeof(*m->ifaces) * opts.num_ifaces);
    for (i = 0; i < opts.num_ifaces; i++) {
      m->ifaces[i] = mg_if_create_iface(opts.ifaces[i], m);
      m->ifaces[i]->vtable->init(m->ifaces[i]);//vtable:虚函数表,保存所有的网络端口的操作函数
    }
  }
  if (opts.nameserver != NULL) {
    m->nameserver = strdup(opts.nameserver);
  }
  DBG(("=================================="));
  DBG(("init mgr=%p", m));
#if MG_ENABLE_SSL
  {
    static int init_done;
    if (!init_done) {
      mg_ssl_if_init();
      init_done++;
    }
  }
#endif
}
  1. mg_connection
    对于保存在连接管理器中的每一个连接,都用一个结构体来表示,即mg_connection
struct mg_connection {
  struct mg_connection *next, *prev; /* mg_mgr::active_connections linkage 双向链表,指向前一个和后一个连接*/
  struct mg_connection *listener;    /* Set only for accept()-ed connections 当连接为监听连接时,设置为0;当连接是由监听连接创建的连接时,设置为1*/
  struct mg_mgr *mgr;                /* Pointer to containing manager 该连接所属的连接管理器*/

  sock_t sock; /* Socket to the remote peer本机套接字 */
  int err;
  union socket_address sa; /* Remote peer address 客户端地址*/
  size_t recv_mbuf_limit;  /* Max size of recv buffer 最大接收缓存量*/
  struct mbuf recv_mbuf;   /* Received data 接收的数据*/
  struct mbuf send_mbuf;   /* Data scheduled for sending 发送的数据*/
  time_t last_io_time;     /* Timestamp of the last socket IO 上一次读写数据*/
  double ev_timer_time;    /* Timestamp of the future MG_EV_TIMER 下一次出发的时间?*/
#if MG_ENABLE_SSL
  void *ssl_if_data; /* SSL library data. */
#endif
  mg_event_handler_t proto_handler; /* Protocol-specific event handler 协议定义的事件处理函数*/
  void *proto_data;                 /* Protocol-specific data 特定于协议的数据*/
  void (*proto_data_destructor)(void *proto_data);
  mg_event_handler_t handler; /* Event handler function 事件处理函数*/
  void *user_data;            /* User-specific data 用户数据*/
  union {
    void *v;
    /*
     * the C standard is fussy about fitting function pointers into
     * void pointers, since some archs might have fat pointers for functions.
     */
    mg_event_handler_t f;
  } priv_1;
  void *priv_2;
  void *mgr_data; /* Implementation-specific event manager's data. 特定于实现的事件管理器的数据*/
  struct mg_iface *iface;
  unsigned long flags;
/* Flags set by Mongoose 各个标志位,用于后面的事件处理函数*/
#define MG_F_LISTENING (1 << 0)          /* This connection is listening */
#define MG_F_UDP (1 << 1)                /* This connection is UDP */
#define MG_F_RESOLVING (1 << 2)          /* Waiting for async resolver */
#define MG_F_CONNECTING (1 << 3)         /* connect() call in progress */
#define MG_F_SSL (1 << 4)                /* SSL is enabled on the connection */
#define MG_F_SSL_HANDSHAKE_DONE (1 << 5) /* SSL hanshake has completed */
#define MG_F_WANT_READ (1 << 6)          /* SSL specific */
#define MG_F_WANT_WRITE (1 << 7)         /* SSL specific */
#define MG_F_IS_WEBSOCKET (1 << 8)       /* Websocket specific */

/* Flags that are settable by user */
#define MG_F_SEND_AND_CLOSE (1 << 10)      /* Push remaining data and close  */
#define MG_F_CLOSE_IMMEDIATELY (1 << 11)   /* Disconnect */
#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */
#define MG_F_DELETE_CHUNK (1 << 13)        /* HTTP specific */
#define MG_F_ENABLE_BROADCAST (1 << 14)    /* Allow broadcast address usage */

#define MG_F_USER_1 (1 << 20) /* Flags left for application */
#define MG_F_USER_2 (1 << 21)
#define MG_F_USER_3 (1 << 22)
#define MG_F_USER_4 (1 << 23)
#define MG_F_USER_5 (1 << 24)
#define MG_F_USER_6 (1 << 25)
};

接下来一行

mg_bind(&mgr, "1234", ev_handler);

在端口1234上创建一个监听连接,并绑定一个回调函数,然后加入到刚才创建的连接管理器中。
接下来看代码:

struct mg_connection *mg_bind(struct mg_mgr *srv, const char *address,
                              MG_CB(mg_event_handler_t event_handler,
                                    void *user_data)) {
  struct mg_bind_opts opts;
  memset(&opts, 0, sizeof(opts));
  return mg_bind_opt(srv, address, MG_CB(event_handler, user_data), opts);
}

struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
                                  MG_CB(mg_event_handler_t callback,
                                        void *user_data),
                                  struct mg_bind_opts opts) {
  union socket_address sa;
  struct mg_connection *nc = NULL;
  int proto, rc;
  struct mg_add_sock_opts add_sock_opts;
  char host[MG_MAX_HOST_LEN];

#if MG_ENABLE_CALLBACK_USERDATA
  opts.user_data = user_data;
#endif

  if (callback == NULL) {
    MG_SET_PTRPTR(opts.error_string, "handler is required");
    return NULL;
  }

  MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts);
//解析地址,这里由于只有端口号,则直接使用本机地址
  if (mg_parse_address(address, &sa, &proto, host, sizeof(host)) <= 0) {
    MG_SET_PTRPTR(opts.error_string, "cannot parse address");
    return NULL;
  }
//创建一个连接,并设置该连接的回调函数
  nc = mg_create_connection(mgr, callback, add_sock_opts);
  if (nc == NULL) {
    return NULL;
  }

  nc->sa = sa;
  //将标志位设置为监听状态
  nc->flags |= MG_F_LISTENING;
  if (proto == SOCK_DGRAM) nc->flags |= MG_F_UDP;

#if MG_ENABLE_SSL
  DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"),
       (opts.ssl_key ? opts.ssl_key : "-"),
       (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-")));

  if (opts.ssl_cert != NULL || opts.ssl_ca_cert != NULL) {
    const char *err_msg = NULL;
    struct mg_ssl_if_conn_params params;
    if (nc->flags & MG_F_UDP) {
      MG_SET_PTRPTR(opts.error_string, "SSL for UDP is not supported");
      mg_destroy_conn(nc, 1 /* destroy_if */);
      return NULL;
    }
    memset(&params, 0, sizeof(params));
    params.cert = opts.ssl_cert;
    params.key = opts.ssl_key;
    params.ca_cert = opts.ssl_ca_cert;
    params.cipher_suites = opts.ssl_cipher_suites;
    if (mg_ssl_if_conn_init(nc, &params, &err_msg) != MG_SSL_OK) {
      MG_SET_PTRPTR(opts.error_string, err_msg);
      mg_destroy_conn(nc, 1 /* destroy_if */);
      return NULL;
    }
    nc->flags |= MG_F_SSL;
  }
#endif /* MG_ENABLE_SSL */
//绑定套接字并将套接字设置为监听状态
  if (nc->flags & MG_F_UDP) {
    rc = nc->iface->vtable->listen_udp(nc, &nc->sa);
  } else {
    rc = nc->iface->vtable->listen_tcp(nc, &nc->sa);
  }
  if (rc != 0) {
    DBG(("Failed to open listener: %d", rc));
    MG_SET_PTRPTR(opts.error_string, "failed to open listener");
    mg_destroy_conn(nc, 1 /* destroy_if */);
    return NULL;
  }
  mg_add_conn(nc->mgr, nc);

  return nc;
}

到这一步一个连接管理器和一个连接就创建好了。
下面就开始循环遍历每个连接,并处理各种事件。

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

//轮询所有网络接口
int mg_mgr_poll(struct mg_mgr *m, int timeout_ms) {
  int i, num_calls_before = m->num_calls;

  for (i = 0; i < m->num_ifaces; i++) {
    m->ifaces[i]->vtable->poll(m->ifaces[i], timeout_ms);
  }

  return (m->num_calls - num_calls_before);
}

其中虚函数表中的poll函数其实是mg_socket_if_poll
定义如下

/* clang-format off */
#define MG_SOCKET_IFACE_VTABLE                                          \
  {                                                                     \
    mg_socket_if_init,                                                  \
    mg_socket_if_free,                                                  \
    mg_socket_if_add_conn,                                              \
    mg_socket_if_remove_conn,                                           \
    mg_socket_if_poll,                                                  \
    mg_socket_if_listen_tcp,                                            \
    mg_socket_if_listen_udp,                                            \
    mg_socket_if_connect_tcp,                                           \
    mg_socket_if_connect_udp,                                           \
    mg_socket_if_tcp_send,                                              \
    mg_socket_if_udp_send,                                              \
    mg_socket_if_tcp_recv,                                              \
    mg_socket_if_udp_recv,                                              \
    mg_socket_if_create_conn,                                           \
    mg_socket_if_destroy_conn,                                          \
    mg_socket_if_sock_set,                                              \
    mg_socket_if_get_conn_addr,                                         \
  }
/* clang-format on */

mg_socket_if_poll源码如下:

time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) {
  struct mg_mgr *mgr = iface->mgr;
  double now = mg_time();
  double min_timer;
  struct mg_connection *nc, *tmp;
  struct timeval tv;
  //创建文件描述符清单
  fd_set read_set, write_set, err_set;
  sock_t max_fd = INVALID_SOCKET;
  int num_fds, num_ev, num_timers = 0;
#ifdef __unix__
  int try_dup = 1;
#endif

  FD_ZERO(&read_set);
  FD_ZERO(&write_set);
  FD_ZERO(&err_set);
#if MG_ENABLE_BROADCAST
  mg_add_to_set(mgr->ctl[1], &read_set, &max_fd);
#endif

  /*
   * Note: it is ok to have connections with sock == INVALID_SOCKET in the list,
   * e.g. timer-only "connections".
   */
  min_timer = 0;
  for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) {
    tmp = nc->next;
	//处理所有的活动连接
    if (nc->sock != INVALID_SOCKET) {
      num_fds++;//统计有效的连接

#ifdef __unix__
      /* A hack to make sure all our file descriptos fit into FD_SETSIZE. */
      if (nc->sock >= (sock_t) FD_SETSIZE && try_dup) {
        int new_sock = dup(nc->sock);
        if (new_sock >= 0) {
          if (new_sock < (sock_t) FD_SETSIZE) {
            closesocket(nc->sock);
            DBG(("new sock %d -> %d", nc->sock, new_sock));
            nc->sock = new_sock;
          } else {
            closesocket(new_sock);
            DBG(("new sock is still larger than FD_SETSIZE, disregard"));
            try_dup = 0;
          }
        } else {
          try_dup = 0;
        }
      }
#endif
	  //加入到监听清单中
	  //如果接受缓存没写满,并且不是udp连接或者是监听连接,加入读清单中
      if (nc->recv_mbuf.len < nc->recv_mbuf_limit &&
          (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) {
        mg_add_to_set(nc->sock, &read_set, &max_fd);
      }
	  //如果是客户端连接服务器的连接并且不希望读标志位置位,或者发送缓存大于0且不是连接服务器连接
      if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) ||
          (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) {
        mg_add_to_set(nc->sock, &write_set, &max_fd);
        mg_add_to_set(nc->sock, &err_set, &max_fd);
      }
    }

	//取最小的轮询时间
    if (nc->ev_timer_time > 0) {
      if (num_timers == 0 || nc->ev_timer_time < min_timer) {
        min_timer = nc->ev_timer_time;
      }
      num_timers++;
    }
  }

  /*
   * If there is a timer to be fired earlier than the requested timeout,
   * adjust the timeout.
   */
  if (num_timers > 0) {
    double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */;
    if (timer_timeout_ms < timeout_ms) {
      timeout_ms = (int) timer_timeout_ms;
    }
  }
  if (timeout_ms < 0) timeout_ms = 0;

  tv.tv_sec = timeout_ms / 1000;
  tv.tv_usec = (timeout_ms % 1000) * 1000;
  //select轮询查看每个链接是否活动
  num_ev = select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv);
  now = mg_time();
#if 0
  DBG(("select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev, num_fds,
       timeout_ms));
#endif

#if MG_ENABLE_BROADCAST
  if (num_ev > 0 && mgr->ctl[1] != INVALID_SOCKET &&
      FD_ISSET(mgr->ctl[1], &read_set)) {
    mg_mgr_handle_ctl_sock(mgr);
  }
#endif
  //遍历每一个链接,并检查是否有活动发生
  for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
    int fd_flags = 0;
    if (nc->sock != INVALID_SOCKET) {
      if (num_ev > 0) {
		//如果连接有变化,将fd_flags置为1
        fd_flags = (FD_ISSET(nc->sock, &read_set) &&
                            (!(nc->flags & MG_F_UDP) || nc->listener == NULL)
                        ? _MG_F_FD_CAN_READ
                        : 0) |
                   (FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE : 0) |
                   (FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0);
      }
#if MG_LWIP
      /* With LWIP socket emulation layer, we don't get write events for UDP */
      if ((nc->flags & MG_F_UDP) && nc->listener == NULL) {
        fd_flags |= _MG_F_FD_CAN_WRITE;
      }
#endif
    }
    tmp = nc->next;
    //处理每个事件
    mg_mgr_handle_conn(nc, fd_flags, now);
  }

  return (time_t) now;
}

上面代码的思路也很简单,及检查每个有效连接,对每个连接调用mg_mgr_handle_conn处理函数

void mg_mgr_handle_conn(struct mg_connection *nc, int fd_flags, double now) {
  int worth_logging =
      fd_flags != 0 || (nc->flags & (MG_F_WANT_READ | MG_F_WANT_WRITE));
  if (worth_logging) {
    DBG(("%p fd=%d fd_flags=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock,
         fd_flags, nc->flags, (int) nc->recv_mbuf.len,
         (int) nc->send_mbuf.len));
  }
  //如果是poll事件
  if (!mg_if_poll(nc, now)) return;
  //如果是出站连接事件(连接服务器)
  if (nc->flags & MG_F_CONNECTING) {
    if (fd_flags != 0) {
      int err = 0;
#if !defined(MG_ESP8266)
      if (!(nc->flags & MG_F_UDP)) {//如果是TCP连接
        socklen_t len = sizeof(err);
        int ret =
            getsockopt(nc->sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len);
        if (ret != 0) {
          err = 1;
        } else if (err == EAGAIN || err == EWOULDBLOCK) {
          err = 0;
        }
      }
#else
      /*
       * On ESP8266 we use blocking connect.
       */
      err = nc->err;
#endif
      mg_if_connect_cb(nc, err);//调用连接事件的回调函数
    } else if (nc->err != 0) {
      mg_if_connect_cb(nc, nc->err);
    }
  }
  //如果是可读事件
  if (fd_flags & _MG_F_FD_CAN_READ) {
    if (nc->flags & MG_F_UDP) {
      mg_if_can_recv_cb(nc);//如果是UDP连接
    } else {//TCP连接
      if (nc->flags & MG_F_LISTENING) {//如果是监听到TCP连接
        /*
         * We're not looping here, and accepting just one connection at
         * a time. The reason is that eCos does not respect non-blocking
         * flag on a listening socket and hangs in a loop.
         */
        mg_accept_conn(nc);
      } else {
        mg_if_can_recv_cb(nc);
      }
    }
  }
  //如果是可写事件
  if (fd_flags & _MG_F_FD_CAN_WRITE) mg_if_can_send_cb(nc);

  if (worth_logging) {
    DBG(("%p after fd=%d nc_flags=0x%lx rmbl=%d smbl=%d", nc, nc->sock,
         nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len));
  }
}

对于本例,监听连接上有客户端连接申请到来时,调用mg_accept_conn(nc);创建一个新的连接,并进行初始化,然后加入到连接管理器中。这个创建的新的连接使用监听连接的回调函数,即

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

至此,连接的创建和初始化流程全部列出。下面就是如何发送和接收数据了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值