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 服务器实验

输入 ,得到客户机的浏览器中的连接请求结果

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, "http://www.likecs.com/"); } while(dirent = readdir(dir)) { //读取目录; alarm(TIMEOUT); fprintf(f, "<p><a href="http://www.likecs.com/%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("http://www.likecs.com/"); 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;该语句不会执行到;    } 队友链接

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzswpj.html