Chinaunix首页 | 论坛 | 博客
  • 博客访问: 216085
  • 博文数量: 52
  • 博客积分: 15
  • 博客等级: 民兵
  • 技术积分: 390
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-06 09:31
文章分类

全部博文(52)

文章存档

2015年(1)

2014年(44)

2013年(7)

我的朋友

分类: 嵌入式

2014-05-30 09:57:31

Uip的Webserver比较复杂,用c语言实现一个简单服务器的所有功能,路由功能,GET传参,动态页面生成等。
 
要运行Uip的WebServer 只需要:
1. 修改uip-con.h 里的#inlcude "webserver.h"  去除其注释
2. 在User/main.c 里加入      httpd_init();   //初始化服务器
 
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数据流:
01 static  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()函数根据这个结构体获得要输出的页面名字,做相应处理:
01 static 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,用于读取相应页面的数据,将页面数据存入全局结构体中,是实现路由遍历的关键函数:
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;
01   //遍历所有的页面数据, 方便校验是否存在该页面
02 for(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 }
17 return 0;
18 }
http-fsdata.c 中包含了所有页面的数据。这里的页面数据都转换为ACAll存在数组中,还包括了层叠样式表 (.css文件) 和图片。其数组结构如下:
01 static 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,
21 0};
需要注意的是以下的一段程序:
01 //结构体格式说明:      下一个页面地址(用于遍历网页)    ,网页name地址      ,html数据起始地址          ,html数据长度
02 //其中的加减操作是为了去除文件名的长度
03 const struct httpd_fsdata_file file_processes_shtml[] = {{NULL, data_processes_shtml, data_processes_shtml + 17, sizeof(data_processes_shtml) - 17}};
04  
05 const struct httpd_fsdata_file file_404_html[] = {{file_processes_shtml, data_404_html, data_404_html + 10, sizeof(data_404_html) - 10}};
06  
07 const struct httpd_fsdata_file file_files_shtml[] = {{file_404_html, data_files_shtml, data_files_shtml + 13, sizeof(data_files_shtml) - 13}};
08  
09 const struct httpd_fsdata_file file_footer_html[] = {{file_files_shtml, data_footer_html, data_footer_html + 13, sizeof(data_footer_html) - 13}};
10  
11 const struct httpd_fsdata_file file_header_html[] = {{file_footer_html, data_header_html, data_header_html + 13, sizeof(data_header_html) - 13}};
12  
13 const struct httpd_fsdata_file file_index_html[] = {{file_header_html, data_index_html, data_index_html + 12, sizeof(data_index_html) - 12}};
14  
15 const struct httpd_fsdata_file file_style_css[] = {{file_index_html, data_style_css, data_style_css + 11, sizeof(data_style_css) - 11}};
16  
17 const struct httpd_fsdata_file file_tcp_shtml[] = {{file_style_css, data_tcp_shtml, data_tcp_shtml + 11,sizeof(data_tcp_shtml) - 11}};
18  
19 const struct httpd_fsdata_file file_fade_png[] = {{file_tcp_shtml, data_fade_png, data_fade_png + 10, sizeof(data_fade_png) - 10}};
20  
21 const 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

Network statistics

03
04
05
									
06 IP           Packets received
07              Packets sent
08          Packets dropped
09 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
%! net-stats
									
29
30
31 %!: /footer.html
这是实现动态页面的关键。
 
handle_output()函数中,找到相应页面数据后,若页面为.shtml,则会调用handle_script()函数:
01 static  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中做相应的数据处理,实现动态网页。
阅读(1314) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~