关闭

Uip WebServer 实现

149人阅读 评论(0) 收藏 举报
分类:
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中定义了一些字符阿斯克码,含义如下
#define ISO_nl      0x0a	 // 换行
#define ISO_space   0x20	 // 空格
#define ISO_bang    0x21	 // !
#define ISO_percent 0x25	 // %
#define ISO_period  0x2e     // .
#define ISO_slash   0x2f	 // /
#define ISO_colon   0x3a	 // :
当Uip接收到Uip接收到底层传递的数据,将接收到的数据通过调用http_appcall(),传递给Webserver处理,再通过handle_connection()先后调用handle_input()函数和handle_output()函数

 
handle_input()主要作用是分析http数据流:
static  PT_THREAD(handle_input(struct httpd_state *s))		//分析http数据流, http数据流格式(eg. "GET /6?user=123 HTTP/ ...")
{
  char * ptr;

  PSOCK_BEGIN(&s->sin);

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

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

  PSOCK_READTO(&s->sin, ISO_space);

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

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

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

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

    if(strncmp(s->inputbuf, http_referer, 8) == 0) {
      s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;
      /*      httpd_log(&s->inputbuf[9]);*/
    }
  }
  
  PSOCK_END(&s->sin);
}
分析数据得出访问页面的名字,存入一个全局的结构体中,handle_output()函数根据这个结构体获得要输出的页面名字,做相应处理:
static PT_THREAD(handle_output(struct httpd_state *s))
{
  char *ptr;
  
  PT_BEGIN(&s->outputpt);
 
  if(!httpd_fs_open(s->filename, &s->file)) {   		 //没有此页面,打开404页面
    httpd_fs_open(http_404_html, &s->file);
    strcpy(s->filename, http_404_html);
    PT_WAIT_THREAD(&s->outputpt,
		   send_headers(s,
		   http_header_404));
    PT_WAIT_THREAD(&s->outputpt,
		   send_file(s));
  } else {								 //正常打印相应页面
    PT_WAIT_THREAD(&s->outputpt,						 
		   send_headers(s,
		   http_header_200));
    ptr = strchr(s->filename, ISO_period);				//查找字符串s->filename中首次出现字符ISO_period的位置,返回首次出现c的位置的指针
    if(ptr != NULL && strncmp(ptr, http_shtml, 6) == 0) {	 //判断是否为 .shtml网页文件    
      PT_INIT(&s->scriptpt);
      PT_WAIT_THREAD(&s->outputpt, handle_script(s));    //若为 .shtml页面,则调用handle_script()生成动态网页
    } else {							//不是 .shtml(eg.  /index.html),输出该页面数据
      PT_WAIT_THREAD(&s->outputpt,
		     send_file(s));
    }
  }
  PSOCK_CLOSE(&s->sout);
  PT_END(&s->outputpt);
}
httpd_fs_open()定义于httpd-fs.c,用于读取相应页面的数据,将页面数据存入全局结构体中,是实现路由遍历的关键函数:
int  httpd_fs_open(const char *name, struct httpd_fs_file *file)
{
#if HTTPD_FS_STATISTICS
  u16_t i = 0;
#endif /* HTTPD_FS_STATISTICS */
  struct httpd_fsdata_file_noconst *f;
  //遍历所有的页面数据, 方便校验是否存在该页面
for(f = (struct httpd_fsdata_file_noconst *)HTTPD_FS_ROOT; 	   //HTTPD_FS_ROOT 定义于httpd-fsdata.c, 定义了遍历入口 
    f != NULL; 
    f = (struct httpd_fsdata_file_noconst *)f->next) {             //加载下一个页面数据 ,遍历顺序由httpd_fsdata_file结构体中 next决定(见 httpd-fsdata.c)                                                          
        if(httpd_fs_strcmp(name, f->name) == 0) {                  //校验请求的页面是否为此页面 
            file->data = f->data; 
            file->len = f->len; 
        #if HTTPD_FS_STATISTICS 
                ++count[i]; 
        #endif /* HTTPD_FS_STATISTICS */ 
            return 1;
         } 
    #if HTTPD_FS_STATISTICS 
        ++i; 
    #endif /* HTTPD_FS_STATISTICS */ 
} 
return 0; 
}

http-fsdata.c 中包含了所有页面的数据。这里的页面数据都转换为ACAll存在数组中,还包括了层叠样式表 (.css文件) 和图片。其数组结构如下:
static const unsigned char data_404_html[] = {
	/* /404.html */
	0x2f, 0x34, 0x30, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0,			//文件名  /404.html
	
	0x3c, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0xa, 0x20, 0x20, 0x3c, 	       //html文件转码为16进制数据(ASCLL)
	0x62, 0x6f, 0x64, 0x79, 0x20, 0x62, 0x67, 0x63, 0x6f, 0x6c, 
	0x6f, 0x72, 0x3d, 0x22, 0x77, 0x68, 0x69, 0x74, 0x65, 0x22, 
	0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x63, 0x65, 0x6e, 
	0x74, 0x65, 0x72, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 
	0x20, 0x3c, 0x68, 0x31, 0x3e, 0x34, 0x30, 0x34, 0x20, 0x2d, 
	0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 
	0x66, 0x6f, 0x75, 0x6e, 0x64, 0x3c, 0x2f, 0x68, 0x31, 0x3e, 
	0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x33, 
	0x3e, 0x47, 0x6f, 0x20, 0x3c, 0x61, 0x20, 0x68, 0x72, 0x65, 
	0x66, 0x3d, 0x22, 0x2f, 0x22, 0x3e, 0x68, 0x65, 0x72, 0x65, 
	0x3c, 0x2f, 0x61, 0x3e, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 
	0x61, 0x64, 0x2e, 0x3c, 0x2f, 0x68, 0x33, 0x3e, 0xa, 0x20, 
	0x20, 0x20, 0x20, 0x3c, 0x2f, 0x63, 0x65, 0x6e, 0x74, 0x65, 
	0x72, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 
	0x79, 0x3e, 0xa, 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 
0};
需要注意的是以下的一段程序:
//结构体格式说明:  	下一个页面地址(用于遍历网页)    ,网页name地址      ,html数据起始地址          ,html数据长度
//其中的加减操作是为了去除文件名的长度
const struct httpd_fsdata_file file_processes_shtml[] = {{NULL, data_processes_shtml, data_processes_shtml + 17, sizeof(data_processes_shtml) - 17}};

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

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

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

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

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

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

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

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

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

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

#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文件,其中有很多 %! 和 %!: 字符 :
%!: /header.html
<h1>Network statistics</h1>
<center>
<table width="300" border="0">
<tr><td><pre>
IP           Packets received
             Packets sent
	     Packets dropped
IP errors    IP version/header length
             IP length, high byte
             IP length, low byte
             IP fragments
             Header checksum
             Wrong protocol
ICMP	     Packets received
             Packets sent
             Packets dropped
             Type errors
TCP          Packets received
             Packets sent
             Packets dropped
             Checksum errors
             Data packets without ACKs
             Resets
             Retransmissions
	     No connection avaliable
	     Connection attempts to closed ports
</pre></td><td><pre>%! net-stats
</pre></table>
</center>
%!: /footer.html
这是实现动态页面的关键。
 
handle_output()函数中,找到相应页面数据后,若页面为.shtml,则会调用handle_script()函数:
static  PT_THREAD(handle_script(struct httpd_state *s))	   							
{
  char *ptr;
  
  PT_BEGIN(&s->scriptpt);


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

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

    ...略去
uip 载入html数据的方法类似网页里的模板引擎的实现方法。当页面输出时,检测到有字符串 %! 和 %!: 时,则调用相应的cgi程序(httpd-cgi.c)处理,在httpd-cgi.c中做相应的数据处理,实现动态网页。
0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

Uip webserver数组数据分析

Uip 1.0 webserver中将html文件转换为数组后存储在fsdata.c文件中。 先分析fsdata_file这个结构体: struct fsdata_file  {   const str...
  • Andy_chen1990
  • Andy_chen1990
  • 2015-05-10 15:48
  • 295

uIP的web服务

转自 程序使用了作者写的webserver的demo程序,添加了相应的注释。说一下整个web服务器实现的流程。uip的web服务的回调函数是/webserver/httpd.c中httpd_appcall(void)。接着进入了handle_connection函数。 static vo...
  • DanielLee_ustb
  • DanielLee_ustb
  • 2014-07-07 15:48
  • 3779

UIP协议栈六 WEB

程序使用了作者写的webserver的demo程序,添加了相应的注释。说一下整个web服务器实现的流程。uip的web服务的回调函数是/webserver/httpd.c中httpd_appcall(void)。接着进入了handle_connection函数。 [cpp] vie...
  • u014186096
  • u014186096
  • 2015-08-19 14:59
  • 815

uIP调试之ping、arp与死机

话说博主我这几天被uIP整的可以说焦头烂额,出了不少问题,在此小结一下。          也是最近一个月才接触使用stm32+DM9000AE组成的百兆网络,与之前enc28j60比起来算是高大上了。uIP在实现TCP...
  • DanielLee_ustb
  • DanielLee_ustb
  • 2014-03-19 22:27
  • 3630

uIP嵌入式TCP/IP协议栈

uIP开源项目介绍 uIP是专为8位和16位的嵌入式微控制器设计的微型TCP/IP协议栈, 它采用BSD授权(这里可以获得完整的BSD许可证), 具有良好的互操作性, 并遵循RFC标准.   uIP提供了网络通信所必须的协议, 本身代码和占用的内存数都非常少 - uIP的源代码只...
  • wang_xya
  • wang_xya
  • 2014-03-04 14:41
  • 1161

uIP TCP Server 运行机制分析

uIP TCP server 运行机制分析 DanielLee_USTB 2013.9.22                  对于任何一个协议栈而言,首先要实现的就是TCP server,下面看看uIP的运行...
  • DanielLee_ustb
  • DanielLee_ustb
  • 2013-09-22 20:07
  • 4701

通过Uip WebClient 实现中应用DNS解析

Uip WebClient 实现 作者:Changing发表时间:07-27 00:42分类:电子相关2 Comments 前一篇:Uip + Stm32移植问题总结 后一篇:Uip WebServer 实现 Uip WebClient 实现的功能是接入互联网,通...
  • mobei1983
  • mobei1983
  • 2016-09-14 10:46
  • 352

uIP无操作系统(裸机)移植

本文转自music_fong博客:http://blog.csdn.net/music_fong/article/details/7191773 以前自己写了一个TCP/IP的协议栈,但是需要的48K的RAM。后来随着产品功能的增加,RAM资源就越来越不够用了。在一个偶然的机会下,发现了一个开...
  • mobei1983
  • mobei1983
  • 2016-11-04 20:33
  • 450

网络通信之UIP在STM32上的移植

1、什么是UIP协议栈: UIP是一种适用于小型嵌入式通信的TCP/IP协议栈,由瑞典计算机科学院的人开发编写的,它去掉了完整的TCP/IP中不常用的功能,简化了通讯过程,但其保留了网络通信中必须使用的协议,把设计重点放在IP、TCP和ARP协议的实现上。简单易用、占用资源少是它的设计特点。...
  • dddxxxx
  • dddxxxx
  • 2016-10-22 13:29
  • 1333

关于uIP移植以及部分特性解析和勘误

关于uIP的移植以及部分特性解析和勘误       关于嵌入式网络的领域,uIP是一个值得去学习的轻量级协议栈,在我的理解里,uIP具有如下特点.   1.封装性好   封装性好体现在uIP它能做到的网络协议栈的底层所做的工作都给完成了,当然包括最基本的数据链路层和网络...
  • dldw8816
  • dldw8816
  • 2016-07-11 14:18
  • 576
    最新评论
    个人资料
    • 访问:66212次
    • 积分:155
    • 等级:
    • 排名:千里之外
    • 原创:6篇
    • 转载:111篇
    • 译文:1篇
    • 评论:3条