分享

Uip WebServer 实现-Changing's Blog

 汉江秋月夜 2014-05-07

Uip WebServer 实现

作者:Changing发表时间:分类:电子相关No Comments

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 页面:
6581bcdegw1dv93b6puvhj.jpg
分析下 Uip Webserver 的运行过程,Uip WebServer使用到相关文件如下:
httpd.c                        
httpd.c中定义了一些字符阿斯克码,含义如下
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数据流:
01static  PT_THREAD(handle_input(struct httpd_state *s))      //分析http数据流, http数据流格式(eg. "GET /6?user=123 HTTP/ ...")
02{
03  char * ptr;
04 
05  PSOCK_BEGIN(&s->sin);
06 
07  //取出到下一个空格号之前的数据
08  PSOCK_READTO(&s->sin, ISO_space);                 //Uip使用这个函数从http数据流中剥离数据
09 
10   
11  if(strncmp(s->inputbuf, http_get, 4) != 0) {             //判断数据流前4位字符是否为"GET ",判断是否为GET传参
12    PSOCK_CLOSE_EXIT(&s->sin);
13  }
14 
15  PSOCK_READTO(&s->sin, ISO_space);
16 
17  if(s->inputbuf[0] != ISO_slash) {               
18    PSOCK_CLOSE_EXIT(&s->sin);
19  }
20 
21  if(s->inputbuf[1] == ISO_space) {                   //请求路径为 "/ " (eg. 192.168.1.15/ ) 更新请求页面filename 为/index.html
22    strncpy(s->filename, http_index_html, sizeof(s->filename));
23  } else {                                        //根据请求路径,更新结构体中filename为相应页面名称
24    s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
25    strncpy(s->filename, &s->inputbuf[0], sizeof(s->filename));      
26  }
27 
28  /*  httpd_log_file(uip_conn->ripaddr, s->filename);*/
29   
30  s->state = STATE_OUTPUT;
31 
32  while(1) {
33    PSOCK_READTO(&s->sin, ISO_nl);
34 
35    if(strncmp(s->inputbuf, http_referer, 8) == 0) {
36      s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;
37      /*      httpd_log(&s->inputbuf[9]);*/
38    }
39  }
40   
41  PSOCK_END(&s->sin);
42}
分析数据得出访问页面的名字,存入一个全局的结构体中,handle_output()函数根据这个结构体获得要输出的页面名字,做相应处理:
01static PT_THREAD(handle_output(struct httpd_state *s))
02{
03  char *ptr;
04   
05  PT_BEGIN(&s->outputpt);
06  
07  if(!httpd_fs_open(s->filename, &s->file)) {          //没有此页面,打开404页面
08    httpd_fs_open(http_404_html, &s->file);
09    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,用于读取相应页面的数据,将页面数据存入全局结构体中,是实现路由遍历的关键函数:
1int  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;
01  //遍历所有的页面数据, 方便校验是否存在该页面
02for(f = (struct httpd_fsdata_file_noconst *)HTTPD_FS_ROOT;     //HTTPD_FS_ROOT 定义于httpd-fsdata.c, 定义了遍历入口
03    f != NULL;
04    f = (struct httpd_fsdata_file_noconst *)f->next) {             //加载下一个页面数据 ,遍历顺序由httpd_fsdata_file结构体中 next决定(见 httpd-fsdata.c)                                                          
05        if(httpd_fs_strcmp(name, f->name) == 0) {                  //校验请求的页面是否为此页面
06            file->data = f->data;
07            file->len = f->len;
08        #if HTTPD_FS_STATISTICS
09                ++count[i];
10        #endif /* HTTPD_FS_STATISTICS */
11            return 1;
12         }
13    #if HTTPD_FS_STATISTICS
14        ++i;
15    #endif /* HTTPD_FS_STATISTICS */
16}
17return 0;
18}
http-fsdata.c 中包含了所有页面的数据。这里的页面数据都转换为ACAll存在数组中,还包括了层叠样式表 (.css文件) 和图片。其数组结构如下:
01static const unsigned char data_404_html[] = {
02    /* /404.html */
03    0x2f, 0x34, 0x30, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0,            //文件名  /404.html
04     
05    0x3c, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0xa, 0x20, 0x20, 0x3c,         //html文件转码为16进制数据(ASCLL)
06    0x62, 0x6f, 0x64, 0x79, 0x20, 0x62, 0x67, 0x63, 0x6f, 0x6c,
07    0x6f, 0x72, 0x3d, 0x22, 0x77, 0x68, 0x69, 0x74, 0x65, 0x22,
08    0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x63, 0x65, 0x6e,
09    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,
210};
需要注意的是以下的一段程序:
01//结构体格式说明:      下一个页面地址(用于遍历网页)    ,网页name地址      ,html数据起始地址          ,html数据长度
02//其中的加减操作是为了去除文件名的长度
03const struct httpd_fsdata_file file_processes_shtml[] = {{NULL, data_processes_shtml, data_processes_shtml + 17, sizeof(data_processes_shtml) - 17}};
04 
05const struct httpd_fsdata_file file_404_html[] = {{file_processes_shtml, data_404_html, data_404_html + 10, sizeof(data_404_html) - 10}};
06 
07const struct httpd_fsdata_file file_files_shtml[] = {{file_404_html, data_files_shtml, data_files_shtml + 13, sizeof(data_files_shtml) - 13}};
08 
09const struct httpd_fsdata_file file_footer_html[] = {{file_files_shtml, data_footer_html, data_footer_html + 13, sizeof(data_footer_html) - 13}};
10 
11const struct httpd_fsdata_file file_header_html[] = {{file_footer_html, data_header_html, data_header_html + 13, sizeof(data_header_html) - 13}};
12 
13const struct httpd_fsdata_file file_index_html[] = {{file_header_html, data_index_html, data_index_html + 12, sizeof(data_index_html) - 12}};
14 
15const struct httpd_fsdata_file file_style_css[] = {{file_index_html, data_style_css, data_style_css + 11, sizeof(data_style_css) - 11}};
16 
17const struct httpd_fsdata_file file_tcp_shtml[] = {{file_style_css, data_tcp_shtml, data_tcp_shtml + 11, sizeof(data_tcp_shtml) - 11}};
18 
19const struct httpd_fsdata_file file_fade_png[] = {{file_tcp_shtml, data_fade_png, data_fade_png + 10, sizeof(data_fade_png) - 10}};
20 
21const struct httpd_fsdata_file file_stats_shtml[] = {{file_fade_png, data_stats_shtml, data_stats_shtml + 13, sizeof(data_stats_shtml) - 13}};
22 
23#define HTTPD_FS_ROOT file_stats_shtml      //设定路由遍历入口页面,一定要保证所有页面都遍历过一次
24 
25#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文件,其中有很多 %! 和 %!: 字符 :
01%!: /header.html
02<h1>Network statistics</h1>
03<center>
04<table width="300" border="0">
05<tr><td><pre>
06IP           Packets received
07             Packets sent
08         Packets dropped
09IP errors    IP version/header length
10             IP length, high byte
11             IP length, low byte
12             IP fragments
13             Header checksum
14             Wrong protocol
15ICMP         Packets received
16             Packets sent
17             Packets dropped
18             Type errors
19TCP          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()函数:
01static  PT_THREAD(handle_script(struct httpd_state *s))                            
02{
03  char *ptr;
04   
05  PT_BEGIN(&s->scriptpt);
06 
07 
08  while(s->file.len > 0) {
09 
10    /* Check if we should start executing a script. */      //检测当前html数据(定义于httpd-fsdata.c)中是否存在字符 %! 和 %!:
11    if(*s->file.data == ISO_percent &&
12       *(s->file.data + 1) == ISO_bang) {                   
13      s->scriptptr = s->file.data + 3;                       
14      s->scriptlen = s->file.len - 3;
15      if(*(s->scriptptr - 1) == ISO_colon) {             //若为 %!:  根据其后变量名,打开并输出相应文件
16    httpd_fs_open(s->scriptptr + 1, &s->file);                //eg.  %!: /header.html  打印/header.html
17    PT_WAIT_THREAD(&s->scriptpt, send_file(s));
18      } else {                                  //若为 %!   根据其后变量名,调用相应处理程序(定义于httpd-cgi.c)
19    PT_WAIT_THREAD(&s->scriptpt,                     //eg. %! file-stats     调用file-stats 绑定的file_stats()函数,打印出相关数据,实现动态网页
20               httpd_cgi(s->scriptptr)(s, s->scriptptr));
21      }
22      next_scriptstate(s);
23       
24      /* The script is over, so we reset the pointers and continue
25     sending the rest of the file. */
26      s->file.data = s->scriptptr;
27      s->file.len = s->scriptlen;
28    } else {                                                //当前html数据不存在 %! 和 %!
29      /* See if we find the start of script marker in the block of HTML
30     to be sent. */
31 
32    ...略去
uip 载入html数据的方法类似网页里的模板引擎的实现方法。当页面输出时,检测到有字符串 %! 和 %!: 时,则调用相应的cgi程序(httpd-cgi.c)处理,在httpd-cgi.c中做相应的数据处理,实现动态网页。

相关文章

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多