在用 mongoose 源码开发的时候,这个初始化函数 mg_mgr_init()则是必须的,我们看下它到底做了哪些初始化操作。
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);
}
我们是这样调用的 mg_mgr_init(&mgr, nullptr); 所以 user_data 指针为 nullptr, 所以mg_mgr_init_opt() 里后两个参数是都是空的。
用户数据指针赋给了入参 m->user_data,接下来就是 interface 相关的赋值了,因为 m->ifaces 是二级指针,所以给它申请了内存:
这里用到了一个全局变量 mg_ifaces,它的定义是这样的
const struct mg_iface_vtable *mg_ifaces[] = {&mg_default_iface_vtable,
};
而这个 mg_default_iface_vtable 就是函数指针的集合,
#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, \}#if MG_NET_IF == MG_NET_IF_SOCKET
const struct mg_iface_vtable mg_default_iface_vtable = MG_SOCKET_IFACE_VTABLE;
#endif
这些函数指针集合就是对应这个包含了函数指针定义的结构体:
struct mg_iface_vtable {void (*init)(struct mg_iface *iface);void (*free)(struct mg_iface *iface);void (*add_conn)(struct mg_connection *nc);void (*remove_conn)(struct mg_connection *nc);time_t (*poll)(struct mg_iface *iface, int timeout_ms);/* Set up a listening TCP socket on a given address. rv = 0 -> ok. */int (*listen_tcp)(struct mg_connection *nc, union socket_address *sa);/* Request that a "listening" UDP socket be created. */int (*listen_udp)(struct mg_connection *nc, union socket_address *sa);/* Request that a TCP connection is made to the specified address. */void (*connect_tcp)(struct mg_connection *nc, const union socket_address *sa);/* Open a UDP socket. Doesn't actually connect anything. */void (*connect_udp)(struct mg_connection *nc);/* Send functions for TCP and UDP. Sent data is copied before return. */int (*tcp_send)(struct mg_connection *nc, const void *buf, size_t len);int (*udp_send)(struct mg_connection *nc, const void *buf, size_t len);int (*tcp_recv)(struct mg_connection *nc, void *buf, size_t len);int (*udp_recv)(struct mg_connection *nc, void *buf, size_t len,union socket_address *sa, size_t *sa_len);/* Perform interface-related connection initialization. Return 1 on ok. */int (*create_conn)(struct mg_connection *nc);/* Perform interface-related cleanup on connection before destruction. */void (*destroy_conn)(struct mg_connection *nc);/* Associate a socket to a connection. */void (*sock_set)(struct mg_connection *nc, sock_t sock);/* Put connection's address into *sa, local (remote = 0) or remote. */void (*get_conn_addr)(struct mg_connection *nc, int remote,union socket_address *sa);
};
所以像源码里发送、接收数据的调用都是这样调用的:
nc->iface->vtable->tcp_send
nc->iface->vtable->tcp_recv
那调用的都是上面的函数指针,如果是启用了 ssl 的可能又是另外一套函数指针接口了。
其实我们还可以从 GDB 里看到:
active_connections 保存的就是 struct mg_connection *con = mg_bind(&mgr, buf, nullptr); 这个con:
然后看mgr 里的 ifaces 是二级指针,看一下它的值:
二级指针,解引用两次得到它里面的内容,而这个 vtable 又是一个指针,它指向了 mg_default_iface_vtable,这个就是函数指针接口的集合了: