Uip WebServer 实现
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                Uip WebServer 实现
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.                        
                                
                            
                            
                            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中定義了一些字符阿斯克碼,含義如下
 
 #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.htmlstrncpy(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_STATISTICSu16_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.html0x3c, 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 receivedPackets sentPackets dropped
IP errors    IP version/header lengthIP length, high byteIP length, low byteIP fragmentsHeader checksumWrong protocol
ICMP	     Packets receivedPackets sentPackets droppedType errors
TCP          Packets receivedPackets sentPackets droppedChecksum errorsData packets without ACKsResetsRetransmissionsNo connection avaliableConnection 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.htmlPT_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 continuesending 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 HTMLto be sent. */...略去 uip 載入html數據的方法類似網頁里的模板引擎的實現方法。當頁面輸出時,檢測到有字符串 %! 和 %!: 時,則調用相應的cgi程序(httpd-cgi.c)處理,在httpd-cgi.c中做相應的數據處理,實現動態網頁。
                            
                        
                        
                        總結
以上是生活随笔為你收集整理的Uip WebServer 实现的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 利用ffmpeg实现rtmp推流直播
- 下一篇: 研发效能度量实践者指南(万字长文)
