Uip WebServer 实现
生活随笔
收集整理的這篇文章主要介紹了
Uip WebServer 实现
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Uip的Webserver比較復(fù)雜,用c語言實(shí)現(xiàn)一個(gè)簡單服務(wù)器的所有功能,路由功能,GET傳參,動(dòng)態(tài)頁面生成等。
要運(yùn)行Uip的WebServer 只需要:
1. 修改uip-con.h 里的#inlcude "webserver.h" ?去除其注釋
2. 在User/main.c 里加入?? ? ?httpd_init(); ? //初始化服務(wù)器
Uip+ stm32移植參見?Uip + Stm32移植問題總結(jié)?
瀏覽器訪問 Uip WebServer 頁面:
分析下 Uip Webserver 的運(yùn)行過程,Uip WebServer使用到相關(guān)文件如下:
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 // :
當(dāng)Uip接收到Uip接收到底層傳遞的數(shù)據(jù),將接收到的數(shù)據(jù)通過調(diào)用http_appcall(),傳遞給Webserver處理,再通過handle_connection()先后調(diào)用handle_input()函數(shù)和handle_output()函數(shù)
handle_input()主要作用是分析http數(shù)據(jù)流:
static PT_THREAD(handle_input(struct httpd_state *s)) //分析http數(shù)據(jù)流, http數(shù)據(jù)流格式(eg. "GET /6?user=123 HTTP/ ...")
{char * ptr;PSOCK_BEGIN(&s->sin);//取出到下一個(gè)空格號(hào)之前的數(shù)據(jù)PSOCK_READTO(&s->sin, ISO_space); //Uip使用這個(gè)函數(shù)從http數(shù)據(jù)流中剝離數(shù)據(jù)if(strncmp(s->inputbuf, http_get, 4) != 0) { ????????//判斷數(shù)據(jù)流前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) { ???//請(qǐng)求路徑為 "/ " (eg. 192.168.1.15/ ) 更新請(qǐng)求頁面filename 為/index.htmlstrncpy(s->filename, http_index_html, sizeof(s->filename));} else { //根據(jù)請(qǐng)求路徑,更新結(jié)構(gòu)體中filename為相應(yīng)頁面名稱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);
}
分析數(shù)據(jù)得出訪問頁面的名字,存入一個(gè)全局的結(jié)構(gòu)體中,handle_output()函數(shù)根據(jù)這個(gè)結(jié)構(gòu)體獲得要輸出的頁面名字,做相應(yīng)處理:
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 { //正常打印相應(yīng)頁面PT_WAIT_THREAD(&s->outputpt, send_headers(s,http_header_200));ptr = strchr(s->filename, ISO_period); //查找字符串s->filename中首次出現(xiàn)字符ISO_period的位置,返回首次出現(xiàn)c的位置的指針if(ptr != NULL && strncmp(ptr, http_shtml, 6) == 0) { //判斷是否為 .shtml網(wǎng)頁文件 PT_INIT(&s->scriptpt);PT_WAIT_THREAD(&s->outputpt, handle_script(s)); //若為 .shtml頁面,則調(diào)用handle_script()生成動(dòng)態(tài)網(wǎng)頁} else { //不是 .shtml(eg. /index.html),輸出該頁面數(shù)據(jù)PT_WAIT_THREAD(&s->outputpt,send_file(s));}}PSOCK_CLOSE(&s->sout);PT_END(&s->outputpt);
} httpd_fs_open()定義于httpd-fs.c,用于讀取相應(yīng)頁面的數(shù)據(jù),將頁面數(shù)據(jù)存入全局結(jié)構(gòu)體中,是實(shí)現(xiàn)路由遍歷的關(guān)鍵函數(shù): 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;
??//遍歷所有的頁面數(shù)據(jù), 方便校驗(yàn)是否存在該頁面
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) { ????????????//加載下一個(gè)頁面數(shù)據(jù) ,遍歷順序由httpd_fsdata_file結(jié)構(gòu)體中 next決定(見 httpd-fsdata.c) ?????????????????????????????????????????????????????????if(httpd_fs_strcmp(name, f->name) == 0) { //校驗(yàn)請(qǐng)求的頁面是否為此頁面 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 中包含了所有頁面的數(shù)據(jù)。這里的頁面數(shù)據(jù)都轉(zhuǎn)換為ACAll存在數(shù)組中,還包括了層疊樣式表 (.css文件) 和圖片。其數(shù)組結(jié)構(gòu)如下: 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文件轉(zhuǎn)碼為16進(jìn)制數(shù)據(jù)(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}; 需要注意的是以下的一段程序: //結(jié)構(gòu)體格式說明: 下一個(gè)頁面地址(用于遍歷網(wǎng)頁) ,網(wǎng)頁name地址 ,html數(shù)據(jù)起始地址 ,html數(shù)據(jù)長度
//其中的加減操作是為了去除文件名的長度
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 //設(shè)定路由遍歷入口頁面,一定要保證所有頁面都遍歷過一次#define HTTPD_FS_NUMFILES 10 //設(shè)定頁面數(shù)量 在 httpd_fs_open() 首先加載?file_stats_shtml頁面數(shù)據(jù) 再加載file_stats_shtml結(jié)構(gòu)體中下一個(gè)網(wǎng)頁的數(shù)據(jù) 也就是file_fade_png的數(shù)據(jù),同理?file_fade_png加載后一個(gè)頁面數(shù)據(jù) ?即?file_tcp_shtml數(shù)據(jù) 。。。 這樣循環(huán)一次 就會(huì)加載所有的頁面,實(shí)現(xiàn)里有遍歷 Uip WebServer的動(dòng)態(tài)網(wǎng)頁生成 在uip/apps/Webserver/http-fs/下有Webserver 頁面未轉(zhuǎn)碼的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
這是實(shí)現(xiàn)動(dòng)態(tài)頁面的關(guān)鍵。 handle_output()函數(shù)中,找到相應(yīng)頁面數(shù)據(jù)后,若頁面為.shtml,則會(huì)調(diào)用handle_script()函數(shù): 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. */ //檢測當(dāng)前html數(shù)據(jù)(定義于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) { //若為 %!: 根據(jù)其后變量名,打開并輸出相應(yīng)文件 httpd_fs_open(s->scriptptr + 1, &s->file); //eg. %!: /header.html 打印/header.htmlPT_WAIT_THREAD(&s->scriptpt, send_file(s));} else { //若為 %! 根據(jù)其后變量名,調(diào)用相應(yīng)處理程序(定義于httpd-cgi.c)PT_WAIT_THREAD(&s->scriptpt, //eg. %! file-stats 調(diào)用file-stats 綁定的file_stats()函數(shù),打印出相關(guān)數(shù)據(jù),實(shí)現(xiàn)動(dòng)態(tài)網(wǎng)頁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 { //當(dāng)前html數(shù)據(jù)不存在 %! 和 %!/* See if we find the start of script marker in the block of HTMLto be sent. */...略去 uip 載入html數(shù)據(jù)的方法類似網(wǎng)頁里的模板引擎的實(shí)現(xiàn)方法。當(dāng)頁面輸出時(shí),檢測到有字符串 %! 和 %!: 時(shí),則調(diào)用相應(yīng)的cgi程序(httpd-cgi.c)處理,在httpd-cgi.c中做相應(yīng)的數(shù)據(jù)處理,實(shí)現(xiàn)動(dòng)態(tài)網(wǎng)頁。
總結(jié)
以上是生活随笔為你收集整理的Uip WebServer 实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 利用ffmpeg实现rtmp推流直播
- 下一篇: 研发效能度量实践者指南(万字长文)