lwIP—httpd源码解析(一)
目录 一、lwIP版本 二、httpd初始化流程 ??2.1 初始化入口 三、处理客户端连接
一、lwIP版本 ??此处使用lwip 2.1.2版本。 二、httpd初始化流程 ??2.1 初始化入口 ??代码路径:lwip-2.1.2\lwip-2.1.2\src\apps\http\httpd.c
void httpd_init(void);
??初始化流程如下图:
??初始化httpd时,按顺序做了以下事情: ??1) 申请一个tcp协议控制块,其中altcp_tcp_new_ip_type和tcp_new_ip_type是等价的。 altcp_pcb和tcp_pcb是等价的。
#define altcp_pcb tcp_pcb
#define altcp_tcp_new_ip_type tcp_new_ip_type
struct altcp_pcb *pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
??tcp协议控制块tcp_pcb是一个重要的结构体,维护了tcp传输层所需要的数据结构,例如ip地址、端口号、滑动窗口、网络IO回调函数、连接管理、重发机制等。结构体定义在tcp.h中; ??2) 设置tcp_pcb中的local_ip和port,同时加入已绑定的tcp控制块链表tcp_bound_pcbs中管理;
struct tcp_pcb *tcp_bound_pcbs;
TCP_REG(&tcp_bound_pcbs, pcb);
??3) 监听tcp控制块。申请一个新的tcp_pcb_listen类型,将state置为LISTEN,把原tcp控制块必要的端口IP等信息传给监听控制块,释放原来的tcp_pcb控制块,并且将此控制块放到监听链表tcp_listen_pcbs中。(此处新申请tcp_pcb_listen控制块的原因是为了节约空间,这是一个裁剪版的tcp_pcb协议控制块,在未连接之前不需要完整的tcp控制块);
TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
?? 4) 调用altcp_accept()把回调函数http_accept()注册到上述监听tcp控制块的accept回调成员中。
void tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
{
LWIP_ASSERT_CORE_LOCKED();
if ((pcb != NULL) && (pcb->state == LISTEN)) {
struct tcp_pcb_listen *lpcb = (struct tcp_pcb_listen *)pcb;
lpcb->accept = accept;
}
}
??总结:在lwip的httpd应用中,使用RAW API接口,创建tcp_pcb的方式初始化tcp端口(在RAW API中,没有socket的概念。开发者为了用户的网络编程习惯,在RAW API的基础上封装了类似linux的网络socket编程接口,定义在lwip-2.1.2\lwip-2.1.2\src\api\sockets.c,不过此socket接口虽然使用简单,但效率不及直接操作tcp_pcb编程,故自带的http应用仍采用RAW API的方式)。 ??监听控制块由TCP内核中的链表tcp_listen_pcbs管理,采用回调函数的方式响应客户端的connect请求。 ??在TCP内核,创建了四个链表存放处于不同TCP状态的tcp_pcb控制块,用于进行管理,代码定义在tcp.c中:
struct tcp_pcb *tcp_bound_pcbs;
union tcp_listen_pcbs_t tcp_listen_pcbs;
struct tcp_pcb *tcp_active_pcbs;
struct tcp_pcb *tcp_tw_pcbs;
struct tcp_pcb **const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
&tcp_active_pcbs, &tcp_tw_pcbs
};
??TCP state一共有十一种,理解TCP流程的核心就是理解这十一种状态的含义以及互相之间的转换,状态定义如下代码所示:
static const char *const tcp_state_str[] = {
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RCVD",
"ESTABLISHED",
"FIN_WAIT_1",
"FIN_WAIT_2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT"
};
??可见在内核中,也是基于状态机的形式管理tcp_pcb协议控制块的。 ??至此,完成了一个tcp协议控制块的初始化(类比linux网络编程的说法,是初始化了一个监听套接字socket,并且等待客户端连接)。 三、处理客户端连接 ??客户端连接时,按网络数据的流向,lwIP内核处理流程如下: ??(其中netif是lwIP抽象出的网卡结构体,用于联系底层硬件驱动和上层软件的桥梁,是一个重要的结构,定义在lwip-2.1.2\lwip-2.1.2\src\core\netif.c)
未完待续===
|