信息安全系统设计基础》实验五实验报告

《信息安全系统设计基础》实验五实验报告

课程:信息安全系统设计基础
班级:1453班
姓名学号:20145301赵嘉鑫、20145335郝昊、20145321曾子誉
指导教师:娄嘉鹏
实验日期:2016.11.10
实验时间:10:10~12:30
实验序号:五
实验名称:通讯协议设计

一、实验目的与要求

  • 学习使用 socket 进行通讯编程的过程,了解一个实际的网络通讯应用程序整体设计,阅读HTTP 协议的相关内容,学习几个重要的网络函数的使用方法。

  • 读懂HTTPD.C 源代码。在此基础上增加一些其他功能。在PC 计算机上使用浏览器测试 嵌入式WEB 服务器的功能。

  • (要求)正确使用连接线等实验仪器,并注意保护实验箱。实验结束之后将实验箱送回。

二、实验内容及代码分析

  • 主要代码

http.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;

	#define O_BINARY 
	#define O_BINARY 0 
	#endif 

	char referrer[128]; 
	int content_length; 

	#define SERVER_PORT 80 

	/*发送HTTP协议数据头,服务器回应http协议数据头,发送纯文本文件信息、gif格式图片、gpeg格式图片信息、html信息、服务器版本信息、文件永不过期信息。*/
	int PrintHeader(FILE *f, int content_type) 
	{ 
			alarm(TIMEOUT); 
			fprintf(f,"HTTP/1.0 200 OKn");
			switch (content_type) 
	{  
			case 't': 
	fprintf(f,"Content-type: text/plainn");
	break; 
			case 'g': 
     fprintf(f,"Content-type: image/gifn"); 
		break; 
	case 'j': 
	fprintf(f,"Content-type: image/jpegn");
	break; 
	case 'h': 
	fprintf(f,"Content-type: text/htmln");
	break; 
	} 
	fprintf(f,"Server: uClinux-httpd 0.2.2n");
	fprintf(f,"Expires: 0n");
	fprintf(f,"n");
	alarm(0); 
	return(0); 
	} 
	//对jpeg格式的文件进行处理;

	int DoJpeg(FILE *f, char *name)
	{ 
	char *buf; 
	FILE * infile; 
	int count; 

	if (!(infile = fopen(name, "r"))) {
	fprintf(stderr, "Unable to open JPEG file %s, %dn", name, errno); 
	fflush(f); 
	alarm(0); 
	return -1; 
	} 

	PrintHeader(f,'j');


	copy(infile,f); /* prints the page */  

	alarm(TIMEOUT); 
	fclose(infile); 
	alarm(0); 

	return 0; 
	} 
	//处理gif格式文件;
	int DoGif(FILE *f, char *name)  
	{ 
		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');

		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');

		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;该语句不会执行到;
				   
				}
  • 注释:在网上查找了alarm函数的含义之后得到如下说明————alarm函数又被称为闹钟函数,alarm()用来设置信号AIGALRM经过seconds(参数)指定的秒数之后传送给目前的进程。需要注意的有两个地方:一个进程只能由一个闹钟时间,如果在调用alarm之前已经设置过闹钟时间,那么任何以前的闹钟都会被新闹钟取代;如果参数seconds设置为0,那么之前的闹钟会被取消。

三、实验步骤及实验结果

  • 开发环境的配置(同实验一相同)

    1. 连接arm开发板;
    2. 建立超级终端;
    3. 启动实验平台;
    4. 修改windows xp系统的ip使得它与arm机的ip在同一网段;
    5. 在red hat中安装arm编译器;
    6. 配置环境变量。
  • 进入07_httptd文件夹中,使用vi编辑器理解源代码。

  • 编译应用程序,修改makefile文件

    分别对文件夹下的copy.c和httpd.c进行编译,出现copy和httpd的可执行文件。

  • 下载调试,使用NFS服务方式将HPPTD下载到开发板上,并拷贝测试用的网页进行调试

  • 本机测试在台式机的浏览器中输入http://192.168.0.121,观察在客户机的浏览器中的链接请求结果 。

四、实验遇到的问题及结果

  • 在编译问题遇到下面的问题。

    为了解决httpd未能自动编译的问题,对hpptd.c文件进行手动多线程编译。
    

五、心得体会

本次实验建立在实验四搭建好的实验平台基础上进行;而且,有了实验四中针对利用make进行自动编译的问题处理经验,本次实验进行得十分顺利。

posted @ 2016-11-15 20:19  20145335郝昊  阅读(488)  评论(3编辑  收藏  举报