lwIP源码解析—httpd(二)
一、简介
1.1 lwip版本
lwip 2.1.2
1.2 代码范围
在lwip中包含了http服务端的实现,文件路径:
lwip-2.1.2\src\apps\http\httpd.c
在源码案例中,只实现了GET方法的例子,POST方法还需要自己增加例子。此处也没有加入TLS层,需要实现HTTPS的还需要自己移植TLS。文件代码两千七百行左右。
二、源码分析
2.1 初始化函数
static void
httpd_init_pcb(struct altcp_pcb *pcb, u16_t port)
{
err_t err;
if (pcb) {
altcp_setprio(pcb, HTTPD_TCP_PRIO);
/* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
err = altcp_bind(pcb, IP_ANY_TYPE, port);
LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
pcb = altcp_listen(pcb);
LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
altcp_accept(pcb, http_accept);
}
}
在创建和绑定好tcp_pcb之后注册http_accept() 函数,当tcp客户端与之握手成功时会回调accept函数建立连接。
/**
* A new incoming connection has been accepted.
*/
static err_t
http_accept(void *arg, struct altcp_pcb *pcb, err_t err)
{
struct http_state *hs;
LWIP_UNUSED_ARG(err);
LWIP_UNUSED_ARG(arg);
LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void *)pcb, arg));
if ((err != ERR_OK) || (pcb == NULL)) {
return ERR_VAL;
}
/* Set priority */
altcp_setprio(pcb, HTTPD_TCP_PRIO);
/* Allocate memory for the structure that holds the state of the
connection - initialized by that function. */
hs = http_state_alloc();
if (hs == NULL) {
LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n"));
return ERR_MEM;
}
hs->pcb = pcb;
/* Tell TCP that this is the structure we wish to be passed for our
callbacks. */
altcp_arg(pcb, hs);
/* Set up the various callback functions */
altcp_recv(pcb, http_recv);
altcp_err(pcb, http_err);
altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
altcp_sent(pcb, http_sent);
return ERR_OK;
}
http_accept() 函数为HTTP连接创建结构体 struct http_state *hs; 用户保存这个连接的信息。同时注册回调函数:
altcp_arg(pcb, hs);
/* Set up the various callback functions */
altcp_recv(pcb, http_recv);
altcp_err(pcb, http_err);
altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
altcp_sent(pcb, http_sent);
altcp_arg(pcb, hs); 表明回调中传的参数是对应的hs,此后,完成了初始化工作。建立TCP连接之后,内核tcp.c会调用http_recv() 接受并且处理数据。
2.2 接收函数
/**
* Data has been received on this pcb.
* For HTTP 1.0, this should normally only happen once (if the request fits in one packet).
*/
static err_t
http_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
{
struct http_state *hs = (struct http_state *)arg;
LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void *)pcb,
(void *)p, lwip_strerr(err)));
if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) {
/* error or closed by other side? */
if (p != NULL) {
/* Inform TCP that we have taken the data. */
altcp_recved(pcb, p->tot_len);
pbuf_free(p);
}
if (hs == NULL) {
/* this should not happen, only to be robust */
LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n"));
}
http_close_conn(pcb, hs);
return ERR_OK;
}
#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
if (hs->no_auto_wnd) {
hs->unrecved_bytes += p->tot_len;
}