天天看點

20145216 20145330 《資訊安全系統設計基礎》 實驗五 簡單嵌入式WEB 伺服器實驗

20145216 20145330 《資訊安全系統設計基礎》 實驗五 簡單嵌入式WEB 伺服器實驗

實驗報告封面

20145216 20145330 《資訊安全系統設計基礎》 實驗五 簡單嵌入式WEB 伺服器實驗
20145216 20145330 《資訊安全系統設計基礎》 實驗五 簡單嵌入式WEB 伺服器實驗

實驗步驟

1、閱讀了解源碼

  • 進入/arm2410cl/exp/basic/07_httpd 目錄,使用vi 編輯器或其他編輯器閱讀了解源代碼
    20145216 20145330 《資訊安全系統設計基礎》 實驗五 簡單嵌入式WEB 伺服器實驗

2、編譯應用程式

  • 運作 make 産生可執行檔案httpd
    20145216 20145330 《資訊安全系統設計基礎》 實驗五 簡單嵌入式WEB 伺服器實驗
20145216 20145330 《資訊安全系統設計基礎》 實驗五 簡單嵌入式WEB 伺服器實驗

3、下載下傳調試

  • 使用 NFS 服務方式将HTTPD 下載下傳到開發闆上,并拷貝測試用的網頁進行調試,本例中用的是index 測試網頁。
    20145216 20145330 《資訊安全系統設計基礎》 實驗五 簡單嵌入式WEB 伺服器實驗
20145216 20145330 《資訊安全系統設計基礎》 實驗五 簡單嵌入式WEB 伺服器實驗

4、本機測試

  • 在桌上型電腦的浏覽器中輸入 http://192.168.0.111(111 為UP-CUP S2410 實驗闆的IP

    位址),觀察在客戶機的浏覽器中的連接配接請求結果(如圖2.7.8)和在開發闆上的伺服器的列印資訊。

  • 本機實驗闆的IP位址經檢視為:
    20145216 20145330 《資訊安全系統設計基礎》 實驗五 簡單嵌入式WEB 伺服器實驗
  • 輸入 HTTP://192.168.0.121,得到客戶機的浏覽器中的連接配接請求結果
    20145216 20145330 《資訊安全系統設計基礎》 實驗五 簡單嵌入式WEB 伺服器實驗

問題與解決

  • 這次實驗是與實驗四同一節實驗課完成,是以不用重新配置環境。按照實驗書步驟一步步進行并沒有遇見很難解決的問題。

實驗感想與體會

  • 這次實驗總體來說我們完成的比較順利。做實驗的魅力就在于要不斷的嘗試與探索,在這個過程中操作更加熟練知識條理更加清晰,我們并不是一味的依賴實驗指導書,邊做我們也在邊思考,避免了一些錯誤發生,要了解檔案在自己計算機内的路徑與實驗闆的IP,不能一直照搬照抄。

httpd.c代碼分析

#include <stdio.h> 
	#include <stdlib.h> 
	#include <fcntl.h> 
	#include <string.h> 
	#include <sys/types.h> 
	#include <sys/socket.h> 
	#include <netinet/in.h> 
	#include <errno.h> 
	#include <sys/stat.h> 
	#include <dirent.h> 
	#include <signal.h> 
	#include <unistd.h> 
	#include <ctype.h> 
	#include "pthread.h" 





	#define DEBUG 
	 
	int KEY_QUIT=0; 
	int TIMEOUT=30; //設定鬧鐘秒數; 
	 
	#ifndef O_BINARY 
	#define O_BINARY 0 
	#endif 
	 
	char referrer[128]; 
	int content_length; 
	 
	#define SERVER_PORT 80 
	 
	int PrintHeader(FILE *f, int content_type)   //發送HTTP協定資料頭 
	{ 
	  alarm(TIMEOUT); 
	  fprintf(f,"HTTP/1.0 200 OKn"); //伺服器回應http協定資料頭的狀态行;發送請求成功; 
	  switch (content_type) 
	  {  
	   case 't': 
	fprintf(f,"Content-type: text/plainn"); //發送純文字檔案資訊; 
	break; 
	   case 'g': 
	fprintf(f,"Content-type: image/gifn"); //發送gif格式圖檔資訊; 
	break; 
	   case 'j': 
	fprintf(f,"Content-type: image/jpegn"); //發送gpeg格式圖檔資訊; 
	break; 
	   case 'h': 
	fprintf(f,"Content-type: text/htmln"); //發送html資訊; 
	break; 
	  } 
	  fprintf(f,"Server: uClinux-httpd 0.2.2n"); //發送伺服器版本資訊; 
	  fprintf(f,"Expires: 0n"); //發送檔案永不過期資訊; 
	  fprintf(f,"n"); //列印換行符; 
	  alarm(0); 
	  return(0); 
	} 
	 
	int DoJpeg(FILE *f, char *name)  //對jpeg格式的檔案進行處理; 
	{ 
	  char *buf; 
	  FILE * infile; 
	  int count; 
	  
	  if (!(infile = fopen(name, "r"))) { //通過檔案名打開一個檔案,隻讀屬性; 
	alarm(TIMEOUT); 
	fprintf(stderr, "Unable to open JPEG file %s, %dn", name, errno); 
	fflush(f); 
	alarm(0); 
	return -1; 
	  } 
	  
	  PrintHeader(f,'j');//發送j類型的http協定資料頭資訊; 
	 
	  
	  copy(infile,f); /* prints the page */  
	  
	  alarm(TIMEOUT); 
	  fclose(infile); 
	  alarm(0); 
	  
	  return 0; 
	} 
	 
	int DoGif(FILE *f, char *name)  //對gif格式的檔案進行處理; 
	{ 
	  char *buf; 
	  FILE * infile; 
	  int count; 
	 
	  if (!(infile = fopen(name, "r"))) { //通過檔案名打開一個檔案,隻讀屬性; 
	alarm(TIMEOUT); 
	fprintf(stderr, "Unable to open GIF file %s, %dn", name, errno); 
	fflush(f); 
	alarm(0); 
	return -1; 
	  } 
	   
	  PrintHeader(f,'g'); //發送g類型的http協定資料頭資訊 
	 
	  copy(infile,f); /* prints the page */   
	 
	  alarm(TIMEOUT); 
	  fclose(infile); 
	  alarm(0); 
	   
	  return 0; 
	} 
	 
	int DoDir(FILE *f, char *name) //對目錄進行處理; 
	{ 
	  char *buf; 
	  DIR * dir; 
	  struct dirent * dirent; //dirent不僅僅指向目錄,還指向目錄中的具體檔案,dirent結構體存儲的關于檔案的資訊很少,是以dirent起着一個索引的作用 
	 
	  if ((dir = opendir(name))== 0) { //打開一個目錄; 
	fprintf(stderr, "Unable to open directory %s, %dn", name, errno); 
	fflush(f); 
	return -1; 
	  } 
	   
	  PrintHeader(f,'h'); //發送h類型的http協定資料頭資訊 
	   
	  alarm(TIMEOUT); 
	  fprintf(f, "<H1>Index of %s</H1>nn",name); 
	  alarm(0); 
	 
	  if (name[strlen(name)-1] != '/') { //若名字的後面沒有/則預設加上 /; 
	strcat(name, "/"); 
	  } 
	   
	  while(dirent = readdir(dir)) { //讀取目錄; 
	alarm(TIMEOUT); 
	   
	fprintf(f, "<p><a href="/%s%s">%s</p>n", name, dirent->d_name, dirent->d_name); 
	alarm(0); //發送目錄資訊; 
	  } 
	   
	  closedir(dir); 
	  return 0; 
	} 
	 
	int DoHTML(FILE *f, char *name) 
	{ 
	  char *buf; 
	  FILE *infile; //定義檔案流指針 
	  int count;  
	  char * dir = 0; 
	 
	  if (!(infile = fopen(name,"r"))) {   //通過檔案名打開一個檔案,隻讀屬性; 
	alarm(TIMEOUT);  
	fprintf(stderr, "Unable to open HTML file %s, %dn", name, errno); //列印打開檔案失敗資訊; 
	fflush(f); 
	alarm(0); 
	return -1; 
	  } 
	 
	  PrintHeader(f,'h'); //發送http協定資料報;f表示客戶連接配接的檔案流指針用于寫入http協定資料頭資訊; 
	  copy(infile,f); /* prints the page */  //将打開的檔案内容通過發送回用戶端; 
	 
	  alarm(TIMEOUT); 
	  fclose(infile); 
	  alarm(0); 
	 
	  return 0; 
	} 
	 
	int DoText(FILE *f, char *name) //純文字檔案的處理; 
	{ 
	  char *buf; 
	  FILE *infile; //定義檔案流指針; 
	  int count; 
	 
	  if (!(infile = fopen(name,"r"))) { //通過檔案名打開一個檔案,隻讀屬性 
	alarm(TIMEOUT); 
	fprintf(stderr, "Unable to open text file %s, %dn", name, errno); 
	fflush(f); 
	alarm(0); 
	return -1; 
	  } 
	 
	  PrintHeader(f,'t'); //發送t類型的http協定資料頭資訊; 
	  copy(infile,f); /* prints the page */   
	 
	  alarm(TIMEOUT); 
	  fclose(infile); 
	  alarm(0); 
	 
	  return 0; 
	} 
	 
	int ParseReq(FILE *f, char *r) 
	{ 
	  char *bp; //定義指針bp; 
	  struct stat stbuf;  
	  char * arg; //參數指針; 
	  char * c; 
	  int e; 
	  int raw; 
	 
	#ifdef DEBUG 
	  printf("req is '%s'n", r); //列印請求指令;例如:GET /img/baidu_sylogo1.gif HTTP/1.1rn 
	#endif 
	   
	  while(*(++r) != ' ');  /*skip non-white space*/ //判斷buf中的内容是否為空跳過非空白; 
	  while(isspace(*r))   //判斷r所在位置的字元是否為空格若為空格則r指向下一個字元; 
	  r++; 
	   
	  while (*r == '/')  //判斷r所在位置的字元是否為/若為空格則r指向下一個字元; 
	  r++; 
	  bp = r; //将r所指向的内容指派給bp bp指向/之後的内容;img/baidu_sylogo1.gif HTTP/1.1rn 
	   
	  while(*r && (*(r) != ' ') && (*(r) != '?')) 
	  r++;//當r不為空,并求 r不為?時r指向下一個字元 
	   
	#ifdef DEBUG 
	  printf("bp='%s' %x, r='%s' n", bp, *bp,r); //列印 r和bp的值; 
	#endif 
	   
	  if (*r == '?')   //判斷 r是否為 ?若為?則執行以下語句; 
	  { 
	  char * e; //定義指針變量; 
	  *r = 0;  //将r所在位置處的字元設為; 的ASCII碼值是0 
	  arg = r+1; //arg指向下一個參數; 
	  if (e = strchr(arg,' '))  
	{ 
	  *e = '';  //如果arg為空則将arg所在位置置為複制給e; 
	  } 
	  } else  
	{ // 如果目前r指向字元不為 '?', 将r指向字元置為 '',  
	  arg = 0;  
	  *r = 0;   // r處設為; 
	} 
	   
	  c = bp;//将bp指派給c; 
	 
	/*zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz*/ 
	  if (c[0] == 0x20){ //判斷c中的字元内容是否為空格;若為空格 
	c[0]='.'; //将.和放入c數組中; 
	c[1]='';  
	} 
	/*zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz*/ 
	if(c[0] == '') strcat(c,"."); //若 c中為則将.連結在c後; 
	 
	if (c && !stat(c, &stbuf))  //通過檔案名c擷取檔案資訊,并儲存在stbuf中
	//傳回值:  執行成功則傳回0,失敗傳回-1,錯誤代碼存于errno
	 
	  { 
	if (S_ISDIR(stbuf.st_mode))//判斷結果是否為特定的值 
	{  
	char * end = c + strlen(c); //end指向c的末尾; 
	strcat(c, "/index.html"); //将/index.html加到c後,後面追加; 
	if (!stat(c, &stbuf)) //通過檔案名c擷取檔案資訊,并儲存在stbuf中 ;成功傳回0; 
	{ 
	DoHTML(f, c); //對html檔案進行處理; 
	}  
	else  
	{ 
	  *end = ''; //将end指向; 
	DoDir(f,c); //若c中沒有"/index.html" 則跳到目錄處理目錄代碼處去執行; 
	} 
	} 
	else if (!strcmp(r - 4, ".gif")) //判斷r中的後四個字元,即判斷檔案類型; 
	  DoGif(f,c);  //若是 gif格式的檔案則跳轉到DoGif對其進行處理; 
	else if (!strcmp(r - 4, ".jpg") || !strcmp(r - 5, ".jpeg")) 
	  DoJpeg(f,c); //若是 jpg或jpeg格式的檔案則跳轉到DoJpeg對其進行處理; 
	else if (!strcmp(r - 4, ".htm") || !strcmp(r - 5, ".html")) 
	DoHTML(f,c); //若是 htm格式的檔案則跳轉到DoHTML處對其進行處理; 
	 else 
	  DoText(f,c);//若是 純文字格式的檔案則跳轉到DoText對其進行處理 
	}  
	else{ 
	  PrintHeader(f,'h'); //發送h類型的http協定資料頭 
	  alarm(TIMEOUT); 
	  fprintf(f, "<html><head><title>404 File Not Found</title></head>n"); //列印出錯資訊 
	fprintf(f, "<body>The requested URL was not found on this server</body></html>n"); 
	  alarm(0); 
	} 
	  return 0; 
	} 
	 
	void sigalrm(int signo) //定時器終止時發送給程序的信号; 
	{ 
	/* got an alarm, exit & recycle */ 
	exit(0); 
	} 
	 
	int HandleConnect(int fd) 
	{ 
	  FILE *f;//定義檔案流FILE結構體指針用來表示與客戶連接配接的檔案流指針; 
	 
	  char buf[160];  //定義緩沖區buf用來存放用戶端的請求指令; 
	  char buf1[160]; //定義緩沖區buf用來存放用戶端的各字段資訊; 
	 
	  f = fdopen(fd,"a+"); //以檔案描述符的形式打開檔案; a+ 以附加方式打開可讀寫的檔案。若檔案不存在,則會建立該檔案,如果檔案存在,寫入的資料會被加到檔案尾後,即檔案原先的内容會被保留。  
	  if (!f) {//若檔案打開失敗則列印出錯資訊; 
	fprintf(stderr, "httpd: Unable to open httpd input fd, error %dn", errno); 
	alarm(TIMEOUT); // 鬧鐘函數成功則傳回上一個鬧鐘時間的剩餘時間,否則傳回0。 出錯傳回-1  
	close(fd);//關閉檔案描述符; 
	alarm(0); //将鬧鐘時間清0; 
	return 0; 
	  } 
	  setbuf(f, 0); //将關閉緩沖區; 
	 
	  alarm(TIMEOUT); //啟用鬧鐘; 
	 
	  if (!fgets(buf, 150, f)) {   //直接通過f讀取150個字元放入以buf為起始位址中,不成功時傳回0則列印出錯資訊;否則fgets成功傳回函數指針列印buf的内容; 
	fprintf(stderr, "httpd: Error reading connection, error %dn", errno); 
	fclose(f); //關閉檔案描述符; 
	alarm(0);   
	return 0; 
	  } 
	#ifdef DEBUG 
	  printf("buf = '%s'n", buf); //列印客戶機發出的請求指令; 
	#endif 
	 
	  alarm(0);  //将鬧鐘時間清0; 
	 
	  referrer[0] = '';//初始化referrer數組; 
	  content_length = -1;  //将資訊長度初始化為-1; 
	 
	 alarm(TIMEOUT);  //設定定時器; 
	//read other line to parse Rrferrer and content_length infomation 
	while (fgets(buf1, 150, f) && (strlen(buf1) > 2)) {  //直接通過f讀取150個字元放入以buf1為起始位址的空間中; 
	  alarm(TIMEOUT); 
	#ifdef DEBUG 
	printf("Got buf1 '%s'n", buf1); //列印buf1中的資訊; 
	#endif 
	if (!strncasecmp(buf1, "Referer:", 8)) {  //将buf1中的前八個字元與字元串Referer:若相等則将将指針指向buf1中的Referer:之後; 
	  char * c = buf1+8; 
	  while (isspace(*c)) //判斷c處是否為空格若為空格則c指向下一個字元; 
	c++; 
	strcpy(referrer, c); //将c所指的記憶體單元的内容複制到referrer數組中; 
	}  
	else if (!strncasecmp(buf1, "Referrer:", 9)) { //将buf1中的前九個字元與字元串Referrer:若相等則将将指針指向buf1中的Referrer:之後; 
	  char * c = buf1+8; 
	  char * c = buf1+9; 
	  while (isspace(*c))  //判斷c處是否為空格若為空格則c指向下一個字元; 
	c++; 
	  strcpy(referrer, c); //将c所指的記憶體單元的内容複制到referrer數組中; 
	}  
	else if (!strncasecmp(buf1, "Content-length:", 15)) { )) { //将buf1中的前15個字元與字元串Content-length:若相等則将将指針指向buf1中的Content-length:之後; 
	 
	  content_length = atoi(buf1+15); //atoi類型轉換将buf1中的内容轉換為整型指派給content_length; 
	}  
	  } 
	  alarm(0); 
	   
	  if (ferror(f)) {  //錯誤資訊輸出; 
	fprintf(stderr, "http: Error continuing reading connection, error %dn", errno); 
	fclose(f); 
	return 0; 
	  } 
	 
	  ParseReq(f, buf); //解析客戶請求函數; 
	 
	  alarm(TIMEOUT); //打開計時器; 
	  fflush(f); //重新整理流; 
	  fclose(f); //關閉檔案流; 
	  alarm(0); 
	  return 1; 
	} 
	 
	 
	 
	void* key(void* data) 
	{ 
	int c; 
	for(;;){ 
	c=getchar(); //從鍵盤輸入一個字元 
	if(c == 'q' || c == 'Q'){ 
	KEY_QUIT=1; 
	exit(10); //若輸入q則退出程式; 
	break; 
	} 
	} 
	 
	} 
	 
	int main(int argc, char *argv[]) 
	{ 
	  int fd, s;   //定義套接字檔案描述符作為客戶機和伺服器之間的通道; 
	  int len;  
	  volatile int true = 1;  //定義volatile類型的變量用來作為指向緩沖區的指針變量; 
	  struct sockaddr_in ec; 
	  struct sockaddr_in server_sockaddr; //定義結構體變量; 
	   
	  pthread_t th_key;//定義線程号; 
	  void * retval;   //用來存儲被等待線程的傳回值。  
	 
	 
	  signal(SIGCHLD, SIG_IGN); //忽略信号量; 
	  signal(SIGPIPE, SIG_IGN); 
	  signal(SIGALRM, sigalrm);  //設定時鐘信号的對應動作; 
	 
	  chroot(HTTPD_DOCUMENT_ROOT);  //改變根目錄;在makefile檔案中指定; 
	  printf("starting httpd...n"); //列印啟用伺服器程式資訊; 
	  printf("press q to quit.n"); 
	//  chdir("/"); 
	 
	  if (argc > 1 && !strcmp(argv[1], "-i")) {// 若argv【1】等于-i strcmp傳回0 并且 argc大于1  執行if下的語句快即關閉檔案描述符; 
	/* I'm running from inetd, handle the request on stdin */ 
	fclose(stderr); 
	HandleConnect(0); //向HandleConnect函數傳入0檔案描述符即标準輸入; 
	exit(0);  
	  } 
	 
	  if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {  //若擷取套接字出錯則将錯誤資訊輸出到标準裝置; 
	perror("Unable to obtain network"); 
	exit(1); 
	  } 
	   
	  if((setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&true,  //此函數用于設定套接口,若成功傳回0,否則傳回錯誤 
	 sizeof(true))) == -1) { 
	perror("setsockopt failed");   //輸出錯誤資訊; 
	exit(1); 
	  } 
	 
	  server_sockaddr.sin_family = AF_INET; //設定ip位址類型; 
	  server_sockaddr.sin_port = htons(SERVER_PORT);  //設定網絡端口; 
	  server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY表示本地任意ip; 
	   
	  if(bind(s, (struct sockaddr *)&server_sockaddr,  //将所監聽的端口号與伺服器的位址、端口綁定;
	 
	 sizeof(server_sockaddr)) == -1)  {  
	perror("Unable to bind socket");//若綁定失敗則列印出錯資訊; 
	exit(1); 
	  } 
	 
	  if(listen(s, 8*3) == -1) { //listen()聲明伺服器處于監聽狀态,并且最多允許有24個用戶端處于連接配接待狀态; 
	perror("Unable to listen"); 
	exit(4); 
	  } 
	 
	   
	   pthread_create(&th_key, NULL, key, 0);   //建立線程; 
	  /* Wait until producer and consumer finish. */ 
	  printf("wait for connection.n"); //列印伺服器等待連結資訊; 
	  while (1) {   
	   
	len = sizeof(ec);//ec結構體變量的長度; 
	if((fd = accept(s, (void *)&ec, &len)) == -1) { //接受客戶機的請求,與客戶機建立連結; 
	  exit(5); 
	  close(s); 
	} 
	HandleConnect(fd); //處理連結函數調用fd 為客戶連接配接檔案描述符;; 
	 
	  } 
	  pthread_join(th_key, &retval); //以阻塞的方式等待thread指定的線程結束。當函數傳回時,被等待線程的資源被收回。如果程序已經結束,那麼該函數會立即傳回。成功傳回0;該語句不會執行到;
	   
	}

           

隊友連結

  • http://www.cnblogs.com/sjy519/p/6131368.html