你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

uIP WebServer 实现

[复制链接]
咕噜 发布时间:2015-4-19 18:08
本帖最后由 咕噜 于 2015-4-19 18:25 编辑

原帖地址
http://www.ichanging.org/uip-webserver.html
这篇帖子写的不错,分享一下。对于理解uIP很有帮助。
Uip的Webserver比较复杂,用c语言实现一个简单服务器的所有功能,路由功能,GET传参,动态页面生成等。
要运行Uip的WebServer 只需要:
1. 修改uip-con.h 里的#inlcude "webserver.h"  去除其注释
2. 在User/main.c 里加入      httpd_init();   //初始化服务器
Uip+ stm32移植参见 Uip + Stm32移植问题总结
浏览器访问 Uip WebServer 页面:

分析下 Uip Webserver 的运行过程,Uip WebServer使用到相关文件如下:

httpd.c                        
httpd.c中定义了一些ASCII码,含义如下
  1. #define ISO_nl      0x0a     // 换行
  2. #define ISO_space   0x20     // 空格
  3. #define ISO_bang    0x21     // !
  4. #define ISO_percent 0x25     // %
  5. #define ISO_period  0x2e     // .
  6. #define ISO_slash   0x2f     // /
  7. #define ISO_colon   0x3a     // :
复制代码

当Uip接收到Uip接收到底层传递的数据,将接收到的数据通过调用http_appcall(),传递给Webserver处理,再通过handle_connection()先后调用handle_input()函数和handle_output()函数
handle_input()主要作用是分析http数据流:

  1. static  PT_THREAD(handle_input(struct httpd_state *s))      //分析http数据流, http数据流格式(eg. "GET /6?user=123 HTTP/ ...")
  2. {
  3.   char * ptr;

  4.   PSOCK_BEGIN(&s->sin);

  5.   //取出到下一个空格号之前的数据
  6.   PSOCK_READTO(&s->sin, ISO_space);                 //Uip使用这个函数从http数据流中剥离数据

  7.    
  8.   if(strncmp(s->inputbuf, http_get, 4) != 0) {             //判断数据流前4位字符是否为"GET ",判断是否为GET传参
  9.     PSOCK_CLOSE_EXIT(&s->sin);
  10.   }

  11.   PSOCK_READTO(&s->sin, ISO_space);

  12.   if(s->inputbuf[0] != ISO_slash) {               
  13.     PSOCK_CLOSE_EXIT(&s->sin);
  14.   }

  15.   if(s->inputbuf[1] == ISO_space) {                   //请求路径为 "/ " (eg. 192.168.1.15/ ) 更新请求页面filename 为/index.html
  16.     strncpy(s->filename, http_index_html, sizeof(s->filename));
  17.   } else {                                        //根据请求路径,更新结构体中filename为相应页面名称
  18.     s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
  19.     strncpy(s->filename, &s->inputbuf[0], sizeof(s->filename));      
  20.   }

  21.   /*  httpd_log_file(uip_conn->ripaddr, s->filename);*/
  22.    
  23.   s->state = STATE_OUTPUT;

  24.   while(1) {
  25.     PSOCK_READTO(&s->sin, ISO_nl);

  26.     if(strncmp(s->inputbuf, http_referer, 8) == 0) {
  27.       s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;
  28.       /*      httpd_log(&s->inputbuf[9]);*/
  29.     }
  30.   }
  31.    
  32.   PSOCK_END(&s->sin);
  33. }
复制代码
分析数据得出访问页面的名字,存入一个全局的结构体中,handle_output()函数根据这个结构体获得要输出的页面名字,做相应处理:
  1. static PT_THREAD(handle_output(struct httpd_state *s))
  2. {
  3.   char *ptr;
  4.    
  5.   PT_BEGIN(&s->outputpt);
  6.   
  7.   if(!httpd_fs_open(s->filename, &s->file)) {          //没有此页面,打开404页面
  8.     httpd_fs_open(http_404_html, &s->file);
  9.     strcpy(s->filename, http_404_html);
  10.     PT_WAIT_THREAD(&s->outputpt,
  11.            send_headers(s,
  12.            http_header_404));
  13.     PT_WAIT_THREAD(&s->outputpt,
  14.            send_file(s));
  15.   } else {                               //正常打印相应页面
  16.     PT_WAIT_THREAD(&s->outputpt,                     
  17.            send_headers(s,
  18.            http_header_200));
  19.     ptr = strchr(s->filename, ISO_period);               //查找字符串s->filename中首次出现字符ISO_period的位置,返回首次出现c的位置的指针
  20.     if(ptr != NULL && strncmp(ptr, http_shtml, 6) == 0) {    //判断是否为 .shtml网页文件   
  21.       PT_INIT(&s->scriptpt);
  22.       PT_WAIT_THREAD(&s->outputpt, handle_script(s));    //若为 .shtml页面,则调用handle_script()生成动态网页
  23.     } else {                            //不是 .shtml(eg.  /index.html),输出该页面数据
  24.       PT_WAIT_THREAD(&s->outputpt,
  25.              send_file(s));
  26.     }
  27.   }
  28.   PSOCK_CLOSE(&s->sout);
  29.   PT_END(&s->outputpt);
  30. }
复制代码

httpd_fs_open()定义于httpd-fs.c,用于读取相应页面的数据,将页面数据存入全局结构体中,是实现路由遍历的关键函数:
  1. int  httpd_fs_open(const char *name, struct httpd_fs_file *file)
  2. {
  3. #if HTTPD_FS_STATISTICS
  4.   u16_t i = 0;
  5. #endif /* HTTPD_FS_STATISTICS */
  6.   struct httpd_fsdata_file_noconst *f;
复制代码


http-fsdata.c 中包含了所有页面的数据。这里的页面数据都转换为ACAll存在数组中,还包括了层叠样式表 (.css文件) 和图片。其数组结构如下:


  1. static const unsigned char data_404_html[] = {
  2.     /* /404.html */
  3.     0x2f, 0x34, 0x30, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0,            //文件名  /404.html
  4.      
  5.     0x3c, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0xa, 0x20, 0x20, 0x3c,         //html文件转码为16进制数据(ASCLL)
  6.     0x62, 0x6f, 0x64, 0x79, 0x20, 0x62, 0x67, 0x63, 0x6f, 0x6c,
  7.     0x6f, 0x72, 0x3d, 0x22, 0x77, 0x68, 0x69, 0x74, 0x65, 0x22,
  8.     0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x63, 0x65, 0x6e,
  9.     0x74, 0x65, 0x72, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20,
  10.     0x20, 0x3c, 0x68, 0x31, 0x3e, 0x34, 0x30, 0x34, 0x20, 0x2d,
  11.     0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20,
  12.     0x66, 0x6f, 0x75, 0x6e, 0x64, 0x3c, 0x2f, 0x68, 0x31, 0x3e,
  13.     0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x33,
  14.     0x3e, 0x47, 0x6f, 0x20, 0x3c, 0x61, 0x20, 0x68, 0x72, 0x65,
  15.     0x66, 0x3d, 0x22, 0x2f, 0x22, 0x3e, 0x68, 0x65, 0x72, 0x65,
  16.     0x3c, 0x2f, 0x61, 0x3e, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65,
  17.     0x61, 0x64, 0x2e, 0x3c, 0x2f, 0x68, 0x33, 0x3e, 0xa, 0x20,
  18.     0x20, 0x20, 0x20, 0x3c, 0x2f, 0x63, 0x65, 0x6e, 0x74, 0x65,
  19.     0x72, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x2f, 0x62, 0x6f, 0x64,
  20.     0x79, 0x3e, 0xa, 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e,
  21. 0};
复制代码

需要注意的是以下的一段程序:
  1. //结构体格式说明:      下一个页面地址(用于遍历网页)    ,网页name地址      ,html数据起始地址          ,html数据长度
  2. //其中的加减操作是为了去除文件名的长度
  3. const struct httpd_fsdata_file file_processes_shtml[] = {{NULL, data_processes_shtml, data_processes_shtml + 17, sizeof(data_processes_shtml) - 17}};

  4. const struct httpd_fsdata_file file_404_html[] = {{file_processes_shtml, data_404_html, data_404_html + 10, sizeof(data_404_html) - 10}};

  5. const struct httpd_fsdata_file file_files_shtml[] = {{file_404_html, data_files_shtml, data_files_shtml + 13, sizeof(data_files_shtml) - 13}};

  6. const struct httpd_fsdata_file file_footer_html[] = {{file_files_shtml, data_footer_html, data_footer_html + 13, sizeof(data_footer_html) - 13}};

  7. const struct httpd_fsdata_file file_header_html[] = {{file_footer_html, data_header_html, data_header_html + 13, sizeof(data_header_html) - 13}};

  8. const struct httpd_fsdata_file file_index_html[] = {{file_header_html, data_index_html, data_index_html + 12, sizeof(data_index_html) - 12}};

  9. const struct httpd_fsdata_file file_style_css[] = {{file_index_html, data_style_css, data_style_css + 11, sizeof(data_style_css) - 11}};

  10. const struct httpd_fsdata_file file_tcp_shtml[] = {{file_style_css, data_tcp_shtml, data_tcp_shtml + 11, sizeof(data_tcp_shtml) - 11}};

  11. const struct httpd_fsdata_file file_fade_png[] = {{file_tcp_shtml, data_fade_png, data_fade_png + 10, sizeof(data_fade_png) - 10}};

  12. const struct httpd_fsdata_file file_stats_shtml[] = {{file_fade_png, data_stats_shtml, data_stats_shtml + 13, sizeof(data_stats_shtml) - 13}};

  13. #define HTTPD_FS_ROOT file_stats_shtml      //设定路由遍历入口页面,一定要保证所有页面都遍历过一次

  14. #define HTTPD_FS_NUMFILES 10                //设定页面数量
复制代码

在 httpd_fs_open() 首先加载 file_stats_shtml页面数据 再加载file_stats_shtml结构体中下一个网页的数据 也就是file_fade_png的数据,同理 file_fade_png加载后一个页面数据  即 file_tcp_shtml数据 。。。 这样循环一次 就会加载所有的页面,实现里有遍历
Uip WebServer的动态网页生成
在uip/apps/Webserver/http-fs/下有Webserver 页面未转码的html文件,其中有很多 %! 和 %!: 字符 :
  1. %!: /header.html
  2. <h1>Network statistics</h1>
  3. <center>
  4. <table width="300" border="0">
  5. <tr><td><pre>
  6. IP           Packets received
  7.              Packets sent
  8.          Packets dropped
  9. IP errors    IP version/header length
  10.              IP length, high byte
  11.              IP length, low byte
  12.              IP fragments
  13.              Header checksum
  14.              Wrong protocol
  15. ICMP         Packets received
  16.              Packets sent
  17.              Packets dropped
  18.              Type errors
  19. TCP          Packets received
  20.              Packets sent
  21.              Packets dropped
  22.              Checksum errors
  23.              Data packets without ACKs
  24.              Resets
  25.              Retransmissions
  26.          No connection avaliable
  27.          Connection attempts to closed ports
  28. </pre></td><td><pre>%! net-stats
  29. </pre></table>
  30. </center>
  31. %!: /footer.html
复制代码
这是实现动态页面的关键。

handle_output()函数中,找到相应页面数据后,若页面为.shtml,则会调用handle_script()函数:
  1. static  PT_THREAD(handle_script(struct httpd_state *s))                             
  2. {
  3.   char *ptr;
  4.    
  5.   PT_BEGIN(&s->scriptpt);


  6.   while(s->file.len > 0) {

  7.     /* Check if we should start executing a script. */      //检测当前html数据(定义于httpd-fsdata.c)中是否存在字符 %! 和 %!:
  8.     if(*s->file.data == ISO_percent &&
  9.        *(s->file.data + 1) == ISO_bang) {                    
  10.       s->scriptptr = s->file.data + 3;                        
  11.       s->scriptlen = s->file.len - 3;
  12.       if(*(s->scriptptr - 1) == ISO_colon) {             //若为 %!:  根据其后变量名,打开并输出相应文件
  13.     httpd_fs_open(s->scriptptr + 1, &s->file);                //eg.  %!: /header.html  打印/header.html
  14.     PT_WAIT_THREAD(&s->scriptpt, send_file(s));
  15.       } else {                                  //若为 %!   根据其后变量名,调用相应处理程序(定义于httpd-cgi.c)
  16.     PT_WAIT_THREAD(&s->scriptpt,                     //eg. %! file-stats     调用file-stats 绑定的file_stats()函数,打印出相关数据,实现动态网页
  17.                httpd_cgi(s->scriptptr)(s, s->scriptptr));
  18.       }
  19.       next_scriptstate(s);
  20.       
  21.       /* The script is over, so we reset the pointers and continue
  22.      sending the rest of the file. */
  23.       s->file.data = s->scriptptr;
  24.       s->file.len = s->scriptlen;
  25.     } else {                                                //当前html数据不存在 %! 和 %!
  26.       /* See if we find the start of script marker in the block of HTML
  27.      to be sent. */

  28.     ...略去
复制代码

uip 载入html数据的方法类似网页里的模板引擎的实现方法。当页面输出时,检测到有字符串 %! 和 %!: 时,则调用相应的cgi程序(httpd-cgi.c)处理,在httpd-cgi.c中做相应的数据处理,实现动态网页。


看一下效果吧


web 访问

web 访问
收藏 评论0 发布时间:2015-4-19 18:08

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版