lwIP源码解析---httpd(二)

本文深入解析lwIP 2.1.2版本中的HTTP服务器实现,包括初始化函数http_accept(),接收函数http_recv(),以及发送函数http_send()。在http_recv()中,HTTP请求被解析,GET和POST方法分别处理。HTTP首部和主体被解析,并通过http_find_file()查找服务器上的文件。 lwIP的HTTP服务端尚不支持POST方法的完整实现和HTTPS。

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;
  } 
在 STM32F4 平台上实现 HTTP POST 请求通常涉及到使用 LwIP(Lightweight IP)协议栈。`httpd_post_begin` 函数是 LwIP HTTP 服务器模块的一部分,用于处理 HTTP POST 请求的开始阶段。该函数的主要职责包括解析请求头、设置回调函数以及初始化与 POST 数据接收相关的状态。 LwIP 是一个开源的 TCP/IP 协议栈,广泛用于嵌入式系统中,尤其是在基于 ARM Cortex-M 系列处理器(如 STM32F4)的平台上。HTTP 服务器模块 (`httpd`) 提供了基本的 Web 服务器功能,而 `httpd_post_begin` 是其中处理 POST 请求的关键函数之一。 以下是 `httpd_post_begin` 函数的一个典型实现示例,展示了其核心逻辑: ```c void httpd_post_begin(void *connection, struct pbuf *p, u16_t len) { struct http_state *hs = (struct http_state *)connection; // 初始化 POST 数据处理的状态 hs->post_content_len = len; hs->post_received_len = 0; hs->post_buf = NULL; hs->post_buf_len = 0; // 设置回调函数以处理后续接收到的数据 hs->callback = httpd_post_receive; // 如果有初始数据,则直接调用接收函数进行处理 if (len > 0) { httpd_post_receive(connection, p); } } ``` 在这个示例中,`httpd_post_begin` 首先获取当前连接的 HTTP 状态结构体 `hs`,然后初始化与 POST 请求相关的一些字段,例如预期接收的数据长度 `post_content_len`、已接收的数据长度 `post_received_len` 以及用于存储接收到的数据的缓冲区 `post_buf` 和其长度 `post_buf_len`。接着,设置了回调函数 `callback` 为 `httpd_post_receive`,这个函数将在后续接收到数据时被调用。如果当前传入的 `pbuf` 中包含数据(即 `len > 0`),则直接调用 `httpd_post_receive` 开始处理这些数据[^1]。 对于具体的 STM32F4 平台,开发者可能需要根据实际硬件配置和项目需求对上述代码进行适当的调整。此外,完整的 LwIP 源码可以在其官方网站或 GitHub 仓库中找到,建议查阅最新的官方文档和源码以获得更详细的实现细节。 ### 相关函数 - `httpd_post_receive`: 处理 HTTP POST 请求中接收到的数据。 - `httpd_post_finished`: 在所有 POST 数据接收完成后调用,用于结束 POST 请求的处理。 ### 示例代码:`httpd_post_receive` ```c void httpd_post_receive(void *connection, struct pbuf *p) { struct http_state *hs = (struct http_state *)connection; struct pbuf *q; // 遍历 pbuf 链表,将接收到的数据复制到缓冲区 for (q = p; q != NULL; q = q->next) { // 将 q->payload 的数据复制到 hs->post_buf 中 // 注意:此处需根据实际情况分配内存并复制数据 memcpy(hs->post_buf + hs->post_buf_len, q->payload, q->len); hs->post_buf_len += q->len; hs->post_received_len += q->len; } // 检查是否已经接收完所有 POST 数据 if (hs->post_received_len == hs->post_content_len) { // 调用用户定义的 POST 数据处理函数 user_post_handler(hs->post_buf, hs->post_buf_len); // 发送响应 httpd_send(connection, "POST request processed successfully", strlen("POST request processed successfully")); } } ``` 以上代码展示了如何在 `httpd_post_receive` 函数中处理接收到的数据,并在所有数据接收完成后调用用户定义的处理函数。最后,通过 `httpd_send` 函数发送一个简单的响应给客户端。 ### 总结 `httpd_post_begin` 是 LwIP HTTP 服务器模块中用于处理 HTTP POST 请求开始阶段的重要函数。它负责初始化必要的状态变量并设置回调函数,以便在后续接收到数据时能够正确地进行处理。开发者可以根据具体的需求和平台特性对该函数及其相关部分进行定制化开发。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

同人与野

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值