温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何构建LwIP raw api 中的http server

发布时间:2021-12-30 10:44:01 来源:亿速云 阅读:468 作者:柒染 栏目:互联网科技

这篇文章将为大家详细讲解有关如何构建LwIP raw api 中的http server,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

下面为构建LwIP RAW API下HTTP SERVER DEMO的学习记录,内容仅为个人设计与理解,所贴代码均为简化版,不保证适用性与准确性。

本人使用的LwIP版本为2.1.2。LwIP应用层直接接口为ALTCP,是TCP之上包了一个抽象层,也可以通过宏开关LWIP_ALTCP使接口直接对应TCP函数。

一、服务初始化

void http_init(void) {   struct altcp_pcb *pcb;   //分配协议控制块   pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);   //挂载接口   altcp_bind(pcb, IP_ANY_TYPE, 80);   //开启监听   pcb = altcp_listen(pcb);   //注册接收回调   altcp_accept(pcb, http_accept); }

初始化的内容就是分配协议控制块,绑定到http端口号80上,然后等待客户端连接。

  • altcp_tcp_new_ip_type函数内调用TCP接口tcp_new_ip_type创建了控制块pcb,然后定义altcp_pcb控制块ret,将pcb裁剪后将几个LwIP关心的部分对接到ret,上层应用只需关心阉割后的ret而无需再看完整的TCP控制块结构体,应用层的callback也只需跟altcp_pcb对接即可。若空间不足,TCP会释放某些非活跃控制块,而altcp只是根据MEMP剩余空间大小不足回复失败

    pcb->callback_arg = altcp_pcb;

    pcb->recv = altcp_tcp_recv;

    pcb->sent = altcp_tcp_sent;

    pcb->errf = altcp_tcp_err;

struct altcp_pcb * altcp_tcp_new_ip_type(u8_t ip_type) {   /* Allocate the tcp pcb first to invoke the priority handling code      if we're out of pcbs */   struct tcp_pcb *tpcb = tcp_new_ip_type(ip_type);   if (tpcb != NULL) {     struct altcp_pcb *ret = altcp_alloc();     if (ret != NULL) {       altcp_tcp_setup(ret, tpcb);       return ret;     } else {       /* altcp_pcb allocation failed -> free the tcp_pcb too */       tcp_close(tpcb);     }   }   return NULL; }

其上还有一个altcp_new_ip_type的分配方式,可以用户自定义alloc函数和参数保存在其调用的altcp_allocator_t结构体变量中。

  • altcp_bind直接操作altcp_tcp_bind,实际使用tcp_bind,将altcp_pcb对应的pcb和http端口号80绑定。TCP遍历已存在的控制块链表,如没有相同的IP和端口号则插入链表。

  • altcp_listen(conn)启动服务器监听状态,其实际功能函数altcp_tcp_listen调用的也是tcp_listen_with_backlog_and_err。由于TCP监听时会用节省资源的tcp_pcb_listen结构体变量替换tcp_pcb,所以需要更新state元素。再将altcp_tcp_accept与tcp_accept对接,作为客户端连接的回调,其作用是当TCP_EVENT_ACCEPT发生时,开辟一个新的altcp_pcb去对接客户端连接的new_tpcb,并执行arg中预设的accept函数(pcb->callback_arg->accept,即altcp_pcb->accept)

static struct altcp_pcb * altcp_tcp_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err) {   struct tcp_pcb *pcb;   struct tcp_pcb *lpcb;   if (conn == NULL) {     return NULL;   }   ALTCP_TCP_ASSERT_CONN(conn);   pcb = (struct tcp_pcb *)conn->state;   lpcb = tcp_listen_with_backlog_and_err(pcb, backlog, err);   if (lpcb != NULL) {     conn->state = lpcb;     tcp_accept(lpcb, altcp_tcp_accept);     return conn;   }   return NULL; }
  • altcp_accept是将上一步中accept阶段TCP传递到LwIP的回调注册到应用层的http_accept,在此函数内处理客户端的连接请求,完成完整的连接操作。在用户层的http_accept中开辟一个变量放置应用的状态以及下层回调的参数,并将其与对应的pcb联系起来,之后就是将已经与TCP连接好的LwIP各个altcp回调与应用层具体处理函数对应起来。

#define HTTP_PRIO TCP_PRIO_MIN static err_t http_accept(void *arg, struct altcp_pcb *pcb, err_t err) {   struct http_st *st = http_state_alloc();   st->pcb = pcb;   altcp_arg(pcb, hs);   altcp_setprio(pcb, HTTP_PRIO);   altcp_recv(pcb, http_recv);   altcp_poll(pcb, http_poll, HTTP_POLL_INT);   altcp_sent(pcb, http_sent);   altcp_err(pcb, http_err);   return ERR_OK; }

至此一个服务的初始化过程基本完成,接下来就是应用层的函数编写。

二、应用层函数功能

应用层在http_recv处理接收数据的回调,其中结构体pbuf是协议栈分解出的数据包。在raw api的数据处理中,需要用户手动执行altcp_recved通知协议栈数据正确接收,并且pbuf_free释放数据包缓存

static err_t http_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) {   struct http_st *st = (struct http_st *)arg;   if((p == NULL) || (err != ERR_OK) || (st == NULL))   {     if(p != NULL)     {       altcp_recved(pcb, p->tot_len);       pbuf_free(p);     }     http_close_conn(pcb, st);     return ERR_OK;   }   altcp_recved(pcb, p->tot_len);   if(st->handle == NULL)   {     err_t parsed = http_parse_request(p, st, pcb);     pbuf_free(p);     if(parsed == ERR_OK)     {       http_send(pcb, st);     }     else if(parsed == ERR_ARG)     {       http_close_conn(pcb, hs);     }   }   else   {     pbuf_free(p);   }   return ERR_OK; }

     http_parse_request函数主要功能为解析接收的HTTP协议指令,对正确的“GET”进行响应。http server预先储存了file_handle列表,当client发对应的uri时传输相应的文件

    #define CRLF "\r\n" static err_t http_parse_request(struct pbuf *p, struct http_st *st, struct altcp_pcb *pcb) {   char *data;   u16_t data_len;   if((st->handle != NULL) || (st->file != NULL))   {     //already started sending     return ERR_USE;   }   //actual data in pbuf   data = (char *)p->payload;   //length of current buffer    data_len = p->len;   /* find first \r\n */   if(lwip_strnstr(data, CRLF, data_len) != NULL)   {     char *sp1, *sp2;     int is_09 = 0;     u16_t left_len, uri_len;     if(strncmp(data, "GET ", 4))     {       //unsupported       http_find_error_file(st, 501);     }     sp1 = data + 3;     //check URI     left_len = (u16_t)(data_len - ((sp1 + 1) - data));     sp2 = lwip_strnstr(sp1 + 1, " ", left_len);     if(sp2 == NULL)     {       sp2 = lwip_strnstr(sp1 + 1, CRLF, left_len);       is_09 = 1;     }     uri_len = (u16_t)(sp2 - (sp1 + 1));     if((sp2 != 0) && (sp2 > sp1))     {       //find the end of HTTP headers       if(lwip_strnstr(data, CRLF CRLF, data_len) != NULL)       {         char *uri = sp1 + 1;         *sp1 = 0;         uri[uri_len] = 0;         return http_find_file(st, uri, is_09);       }     }   }   return http_find_error_file(st, 400); }
    static err_t http_find_error_file(struct http_st *st, u16_t error_nr) {   const char *uri, *uri1, *uri2, *uri3;   if (error_nr == 501) {     uri1 = "/501.html";     uri2 = "/501.htm";     uri3 = "/501.shtml";   } else {     /* 400 (bad request is the default) */     uri1 = "/400.html";     uri2 = "/400.htm";     uri3 = "/400.shtml";   }   if (fs_open(&st->file_handle, uri1) == ERR_OK) {     uri = uri1;   } else if (fs_open(&st->file_handle, uri2) == ERR_OK) {     uri = uri2;   } else if (fs_open(&st->file_handle, uri3) == ERR_OK) {     uri = uri3;   } else {     return ERR_ARG;   }   return http_init_file(st, &st->file_handle, 0, uri, 0, NULL); }

     根据解析的uri,在本地文件中寻找对应name的文件

    #define LWIP_HTTP_MAX_REQUEST_URI_LEN      63 static char http_uri_buf[LWIP_HTTP_MAX_REQUEST_URI_LEN + 1]; static err_t http_find_file(struct http_st *st, const char *uri, int is_09) {   size_t loop;   struct fs_file *file = NULL;   char *params = NULL;   err_t err;   int i;   u8_t tag_check = 0;   //check uri   size_t uri_len = strlen(uri);   if((uri_len > 0) && (uri[uri_len - 1] == '/') &&       ((uri != http_uri_buf) || (uri_len == 1)))   {     size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1);     if(copy_len > 0)     {       MEMCPY(http_uri_buf, uri, copy_len);       http_uri_buf[copy_len] = 0;     }     for(loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++)     {       const char *file_name;       if(copy_len > 0)       {         size_t len_left = sizeof(http_uri_buf) - copy_len - 1;         if(len_left > 0)         {           size_t name_len = strlen(httpd_default_filenames[loop].name);           size_t name_copy_len = LWIP_MIN(len_left, name_len);           MEMCPY(&http_uri_buf[copy_len], httpd_default_filenames[loop].name, name_copy_len);           http_uri_buf[copy_len + name_copy_len] = 0;         }         file_name = http_uri_buf;       }        else       {         file_name = httpd_default_filenames[loop].name;       }       err = fs_open(&st->file_handle, file_name);       if(err == ERR_OK)       {         uri = file_name;         file = &st->file_handle;         tag_check = httpd_default_filenames[loop].shtml;       }     }   }   if(file == NULL)   {     params = (char *)strchr(uri, '?');     if(params != NULL)     {       *params = '\0';       params++;     }     http_cgi_paramcount = -1;     if(httpd_num_cgis && httpd_cgis)     {       for(i = 0; i < httpd_num_cgis; i++)       {         if(strcmp(uri, httpd_cgis[i].pcCGIName) == 0)         {           http_cgi_paramcount = extract_uri_parameters(hs, params);           uri = httpd_cgis[i].pfnCGIHandler(i, http_cgi_paramcount, st->params,                                          st->param_vals);           break;         }       }     }     err = fs_open(&st->file_handle, uri);     if(err == ERR_OK)     {       file = &st->file_handle;     }     else     {       file = http_get_404_file(st, &uri);     }     if(file != NULL)     {       if(file->flags & FS_FILE_FLAGS_SSI)       {         tag_check = 1;       }       else       {         tag_check = http_uri_is_ssi(file, uri);       }     }   }   if(file == NULL)   {     /* None of the default filenames exist so send back a 404 page */     file = http_get_404_file(st, &uri);   }   return http_init_file(st, file, is_09, uri, tag_check, params); }

     将找到的文件挂载到状态量st

    static err_t http_init_file(struct http_st *st, struct fs_file *file, int is_09, const char *uri,                u8_t tag_check, char *params) {   if(file != NULL)   {     if(tag_check)     {       struct http_ssi_state *ssi = http_ssi_state_alloc();       if(ssi != NULL)       {         ssi->tag_index = 0;         ssi->tag_state = TAG_NONE;         ssi->parsed = file->data;         ssi->parse_left = file->len;         ssi->tag_end = file->data;         st->ssi = ssi;       }     }     st->handle = file;     st->file = file->data;     st->left = (u32_t)file->len;     st->retries = 0;     if(is_09 && ((st->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0))     {       /* HTTP/0.9 responses are sent without HTTP header,          search for the end of the header. */       char *file_start = lwip_strnstr(st->file, CRLF CRLF, st->left);       if(file_start != NULL)       {         int diff = file_start + 4 - st->file;         st->file += diff;         st->left -= (u32_t)diff;       }     }   }   else   {     st->handle = NULL;     st->file = NULL;     st->left = 0;     st->retries = 0;   }   if((st->handle == NULL) || ((st->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0))   {     get_http_headers(st, uri);   }   return ERR_OK; }

    关于如何构建LwIP raw api 中的http server就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI