UIP協議棧六 WEB

  • 程序使用了作者寫的webserver的demo程序,添加了相應的註釋。說一下整個web服務器實現的流程。
  • uip的web服務的回調函數是/webserver/httpd.c中httpd_appcall(void)。接着進入了handle_connection函數。
  1. static void  
  2. handle_connection(struct httpd_state *s)  
  3. {  
  4.   handle_input(s);  
  5.   if(s->state == STATE_OUTPUT) {  
  6.     handle_output(s);  
  7.   }  
  8. }  
  • handle_input(s)實際上是指PT_THREAD(handle_input(struct httpd_state *s)),其他還有一些類似的。這是一個輕量級的模擬的線程環境protothreads.詳情請查看。handle_input(s)是解析瀏覽器向服務器請求的內容。輸入ip地址的話,會輸出http_index_html。如請求其他頁面,會把/後面的字符作爲文件名(等待handle_output(s)輸出)。從這個函數可以看出uip只支持‘get’方法,get格式是:/(文件名)?(第一個變量名)=(填的值)&(第二個變量名)。。。如:192.168.0.2/file.html?num1=2&num2=2。接下來就可以按照這個格式得到瀏覽器往服務器發送了什麼數據。有關html語言相關知識請查看。ps.如果要使用‘post’方法就需要按照格式改寫這個函數了。
  1. static  
  2. PT_THREAD(handle_input(struct httpd_state *s))  
  3. {  
  4.   PSOCK_BEGIN(&s->sin);  
  5.   
  6.   PSOCK_READTO(&s->sin, ISO_space);   //等待空格符  
  7.   
  8.     
  9.   if(strncmp(s->inputbuf, http_get, 4) != 0) {  //不是GET就退出結束  
  10.     PSOCK_CLOSE_EXIT(&s->sin);  
  11.   }  
  12.   PSOCK_READTO(&s->sin, ISO_space); //等待空格符  
  13.   
  14.   if(s->inputbuf[0] != ISO_slash) {//不是'/'就退出結束  
  15.     PSOCK_CLOSE_EXIT(&s->sin);  
  16.   }  
  17.   
  18.   if(s->inputbuf[1] == ISO_space) { //如果是空格符就把index_html作爲輸出頁  
  19.     strncpy(s->filename, http_index_html, sizeof(s->filename));  
  20.   } else {  
  21.     s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;  //否則將後面的字符作爲輸出頁  
  22.     strncpy(s->filename, &s->inputbuf[0], sizeof(s->filename));  
  23.   }  
  24.   //可以處理接收到的數據了  
  25.   
  26.   /*  httpd_log_file(uip_conn->ripaddr, s->filename);*/  
  27.     
  28.   s->state = STATE_OUTPUT;  //設置狀態位可以輸出  
  29.   
  30.   while(1) {  
  31.     PSOCK_READTO(&s->sin, ISO_nl);  
  32.   
  33.     if(strncmp(s->inputbuf, http_referer, 8) == 0) {  
  34.       s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;  
  35.       /*      httpd_log(&s->inputbuf[9]);*/  
  36.     }  
  37.   }  
  38.     
  39.   PSOCK_END(&s->sin);  
  40. }  
  • s->state = STATE_OUTPUT之後就到handle_output(s)函數,用做輸出文件的。如果之前得到請求的網頁名在已存的網頁中找不到,就會輸出404(這個真的大名鼎鼎)。接着會判斷一下請求的是否是動態網頁(shtml爲後綴,當然也可以自己定義cgi、asp等等),是的話就轉去handle_script(s)。不是就直接輸出網頁。ps.網頁都是先寫好的,然後按字符轉換成二進制碼,放到httpd_fsdata.c中,注意該文件最下面的結構體是用來遍歷全部網頁文件的,另外添加文件需要更改這裏。還有就是靜態網頁有個http頭需要添加,原demo中有。
  1. static  
  2. PT_THREAD(handle_output(struct httpd_state *s))  
  3. {  
  4.   char *ptr;  
  5.     
  6.   PT_BEGIN(&s->outputpt);  
  7.   
  8.   if(!httpd_fs_open(s->filename, &s->file)) {  //沒有找到對應的頁面  
  9.     httpd_fs_open(http_404_html, &s->file);  
  10.     strcpy(s->filename, http_404_html);  
  11.     PT_WAIT_THREAD(&s->outputpt,  
  12.              send_headers(s,  
  13.              http_header_404));  
  14.     PT_WAIT_THREAD(&s->outputpt,  //等待線程完成(404錯誤頭和內容)打印404頁面  
  15.              send_file(s));  
  16.   } else {  
  17.     PT_WAIT_THREAD(&s->outputpt,  
  18.              send_headers(s,  
  19.              http_header_200));    //等待輸出200成功  
  20.     ptr = strchr(s->filename, ISO_period);  
  21.     if(ptr != NULL && strncmp(ptr, http_shtml, 6) == 0) {  //判斷爲shtml  
  22.       PT_INIT(&s->scriptpt);  
  23.       PT_WAIT_THREAD(&s->outputpt, handle_script(s));    //等待CGI處理動態頁面  
  24.     } else {  
  25.       PT_WAIT_THREAD(&s->outputpt,        //不是的話輸出頁面  
  26.                send_file(s));  
  27.     }  
  28.   }  
  29.   PSOCK_CLOSE(&s->sout);  
  30.   PT_END(&s->outputpt);  
  31. }  
  • 接下來就是動態網頁的處理了,handle_script(s)是這樣處理的:遍歷網頁的字符,碰到%!:(空格)就輸出後面字符對應的文件。碰到%!(空格)就執行後面字符對應的函數(同時還可以加一個外部參數)如:%! analog num1.在這些參數完了之後都要加個回車符。動態網頁與靜態網頁在設計的時候區別就是:後綴與%!:和%!的標識符。
  1. static  
  2. PT_THREAD(handle_script(struct httpd_state *s))  
  3. {  
  4.   char *ptr;  
  5.     
  6.   PT_BEGIN(&s->scriptpt);  
  7.   
  8.   
  9.   while(s->file.len > 0) {  
  10.   
  11.     /* Check if we should start executing a script. */  
  12.     if(*s->file.data == ISO_percent &&  
  13.        *(s->file.data + 1) == ISO_bang) {  
  14.       s->scriptptr = s->file.data + 3;  
  15.       s->scriptlen = s->file.len - 3;  
  16.       if(*(s->scriptptr - 1) == ISO_colon) {  //如果是%!: 打開後面對應的文件並輸出  
  17.      httpd_fs_open(s->scriptptr + 1, &s->file);  
  18.      PT_WAIT_THREAD(&s->scriptpt, send_file(s));  
  19.       } else {  
  20.      PT_WAIT_THREAD(&s->scriptpt,  
  21.                  httpd_cgi(s->scriptptr)(s, s->scriptptr)); //如果是%! 轉去後面對應的程序  
  22.       }                                                                   // s->scriptptr爲%!:後面的字符  
  23.       next_scriptstate(s);  
  24.         
  25.       /* The script is over, so we reset the pointers and continue 
  26.      sending the rest of the file. */  
  27.       s->file.data = s->scriptptr;  
  28.       s->file.len = s->scriptlen;  
  29.     } else {  
  30.       /* See if we find the start of script marker in the block of HTML 
  31.      to be sent. */  
  32.   
  33.       if(s->file.len > uip_mss()) {  
  34.      s->len = uip_mss();  
  35.       } else {  
  36.      s->len = s->file.len;  
  37.       }  
  38.   
  39.       if(*s->file.data == ISO_percent) {  
  40.      ptr = strchr(s->file.data + 1, ISO_percent);  
  41.       } else {  
  42.      ptr = strchr(s->file.data, ISO_percent);  
  43.       }  
  44.       if(ptr != NULL &&  
  45.      ptr != s->file.data) {  
  46.      s->len = (int)(ptr - s->file.data);  
  47.      if(s->len >= uip_mss()) {  
  48.        s->len = uip_mss();  
  49.      }  
  50.       }  
  51.       PT_WAIT_THREAD(&s->scriptpt, send_part_of_file(s));   //等待打印沒有變量部分的頁面  
  52.       s->file.data += s->len;  
  53.       s->file.len -= s->len;  
  54.         
  55.     }  
  56.   }  
  57.     
  58.   PT_END(&s->scriptpt);  
  59. }  
  • 基本差不多了,然後就是到底怎麼處理然後把結果動態輸出到網頁上的了,這些在http-cgi.c文件裏。這個函數也要先定義好,如下:
  1. HTTPD_CGI_CALL(file, "file-stats", file_stats);               //指針,Web數據裏的名字,回調函數的名字  
  2. HTTPD_CGI_CALL(tcp, "tcp-connections", tcp_stats);  
  3. HTTPD_CGI_CALL(net, "net-stats", net_stats);  
  4. static const struct httpd_cgi_call *calls[] = { &file, &tcp, &net, NULL };  
  • 然後遍歷查找函數名,看是否與請求的一致。接着執行PSOCK_GENERATOR_SEND(&s->sout, #,*)#是指向下個函數的指針。*是給下個函數的參數,如果在設計網頁的時候預留個外部參數就可以讓*=strchr(ptr, ' ') + 1(空格後面,所以在設計的時候要注意)。處理完了之後就改把動態結果輸出了。snprintf((char *)uip_appdata, UIP_APPDATA_SIZE, "%8.3f", Analog_Count(f));用的是這個函數,其中analo_count是我自定義的函數。OK!基本上簡單的web動態網頁已經沒有問題了,還有一些不明白的地方可以看工程裏的註釋。如果使用HG 只要clone https://[email protected]/zouw96/uip_http。
  1. static unsigned short  
  2. generate_file_stats(void *arg)  
  3. {  
  4.   char *f = (char *)arg;                                     //arg 爲後面的外部命令參數  
  5.   return snprintf((char *)uip_appdata, UIP_APPDATA_SIZE, "%8.3f", Analog_Count(f));  
  6. }  
  7. /*---------------------------------------------------------------------------*/  
  8. static  
  9. PT_THREAD(file_stats(struct httpd_state *s, char *ptr))  
  10. {  
  11.   PSOCK_BEGIN(&s->sout);  
  12.   
  13.   PSOCK_GENERATOR_SEND(&s->sout, generate_file_stats, strchr(ptr, ' ') + 1);  //指針指向空格後面的外部命令參數arg  
  14.     
  15.   PSOCK_END(&s->sout);  
  16. }  
發佈了28 篇原創文章 · 獲贊 15 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章