实验封面

一、实验内容

1.阅读理解源码
进入07_httpd所在的目录,使用vi编辑器理解源代码。

2.编译应用程序
使用gcc编译器,分别对文件夹下的copy.c和httpd.c进行编译,出现copy和httpd的可执行文件。
                       

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

4.本机测试
在台式机的浏览器中输入http://192.168.0.121,观察在客户机的浏览器中的链接请求结果和在开发板服务器上的打印信息。

二、实验代码理解

httpd.c代码分析

/ * httpd.c:  A very simple http server

* Copyfight (C) 2003  Zou jian guo <ah_zou@163.com>

* Copyright (C) 2000 Lineo, Inc.  (www.lineo.com)

* Copyright (c) 1997-1999 D. Jeff Dionne <jeff@lineo.ca>

* Copyright (c) 1998  Kenneth Albanowski <kjahds@kjahds.com>

* Copyright (c) 1999  Nick Brok <nick@nbrok.iaehv.nl>

*

* This program is free software; you can redistribute it and/or modify

* it under the terms of the GNU General Public License as published by

* the Free Software Foundation; either version 2 of the License, or

* (at your option) any later version.

*

*/

#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;该语句不会执行到;

  

}

三、遇到问题及解决

1.虽然将07_httpd文件中全部拷贝进了bc中,文件夹中拥有Makefile文件,但是还是无法通过make得到该文件夹中copy和httpd的可执行文件。
解决:第一次是觉得Makefile文件中的PATH路径不对,将其改成了/home/bc/07_httpd存盘退出后发现还是无法执行,第二次我们直接使用gcc对其单步进行编译:
armv4l-unknow-linux-gcc –E copy.c –o copy.i
armv4l-unknow-linux-gcc –S copy.i –o copy.s
armv4l-unknow-linux-gcc –c copy.s –o copy.o
对于httpd的编译方式同上。
Make——工程管理器,为了减少重复工作量,“自动编译管理器”,“自动”在于它能根据文件时间戳自动发现更新过的文件而减少编译工作量,同时它通过读入makefile文件的内容来执行大量的编译工作。用户只需要编写一次简答的编译语句就可以了,也就是说以后只要敲入make即可编译全部文件,它大大提高了实际项目的工作效率,几乎所有linux下的项目编程都需要用到它。

四、实验总结
这次实验是简单嵌入式WEB服务器实验,通过实验操作,以及之前我们队copy和httpd代码的预习,让我们对于在ARM开发板上开发一个简单的WEB的过程,以及在ARM开发板上SOCKET网络编程和Linux下signal()函数的使用有了一定的了解。
从实验中更深入的了解了一些linux系统的特色、与其它系统有别的东西,我们受益匪浅,相信对于信息安全系统设计基础这门课程也有很大的帮助,我们会继续努力!

《Java程序设计》第五次实验实验报告的更多相关文章

  1. 20145227《Java程序设计》第3次实验报告

    20145227<Java程序设计>第3次实验报告 实验步骤与内容 一.实验内容 XP基础 XP核心实践 相关工具 二.实验过程 (一)敏捷开发与XP 1.XP是以开发符合客户需要的软件为 ...

  2. 20145227《Java程序设计》第1次实验报告

    20145227<Java程序设计>第1次实验报告 实验步骤与内容 命令行下Java程序开发 1.打开 cmd ,输入 mkdir 20145227 命令建立实验目录,然后输入 cd 20 ...

  3. 20145331 《Java程序设计》第2次实验报告

    20145331 <Java程序设计>第2次实验报告 实验二 Java面向对象程序设计 一.实验内容 1.初步掌握单元测试和TDD 2.理解并掌握面向对象三要素:封装.继承.多态 3.初步 ...

  4. 20145240 《Java程序设计》第三次实验报告

    20145240 <Java程序设计>第三次实验报告 北京电子科技学院(BESTI)实验报告 课程:Java程序设计 班级:1452 指导教师:娄嘉鹏 实验日期:2016.04.22 实验 ...

  5. 20145330《Java程序设计》第四次实验报告

    20145330<Java程序设计>第四次实验报告 实验四 Android环境搭建 实验内容 1.搭建Android环境 2.运行Android 3.修改代码,能输出学号 实验步骤 搭建A ...

  6. 20145320《Java程序设计》第四次实验报告

    20145320<Java程序设计>第四次实验报告 北京电子科技学院(BESTI)实验报告 课程:Java程序设计 班级:1453 指导教师:娄嘉鹏 实验日期:2016.04.26 15: ...

  7. 20145320《Java程序设计》第三次实验报告

    20145320<Java程序设计>第三次实验报告 北京电子科技学院(BESTI)实验报告 课程:Java程序设计 班级:1453 指导教师:娄嘉鹏 实验日期:2016.04.22 15: ...

  8. 20145227《Java程序设计》第2次实验报告

    20145227<Java程序设计>第2次实验报告 实验步骤与内容 一.实验内容 1. 初步掌握单元测试和TDD 2. 理解并掌握面向对象三要素:封装.继承.多态 3. 初步掌握UML建模 ...

  9. 20155217 《Java程序设计》第三次实验报告

    20155217 <Java程序设计>第三次实验报告 实验内容 XP基础 XP核心实践 相关工具 实验要求 没有Linux基础的同学建议先学习<Linux基础入门(新版)>&l ...

  10. 20145310《Java程序设计》第5次实验报告

    20145310<Java程序设计>第5次实验报告 实验要求 掌握Socket程序的编写: 掌握密码技术的使用: 设计安全传输系统. 实验内容 根据所学内容,编写代码实现服务器与客户端 掌 ...

随机推荐

  1. win10中文简体繁体切换快捷键

    win10中文简体繁体切换快捷键Ctrl+Shift+F

  2. GridControl控件的数据显示的样式控制(转)

    如上两图所示,Dev列表控件GridControl默认的格式并没有渐变变色效果,显示的日期数据,也是“yyyy-MM-dd”的格式,而非“yyyy-MM-dd HH:mm:ss”即使对于后面有长格式的 ...

  3. 问题解决——OpenGL超级宝典 第四章 4.5.2 关于freeglut.lib问题的解决过程

    看<OpenGL超级宝典(第四版)>的4.5.2节时遇到了一系列问题,经过不懈努力终于解决,现将过程记录在下,以便查找追思. 在第4.5.2节之前,自己写的的代码都没有使用作者的gltoo ...

  4. 如何解决mysql stop fail的问题

    最近在学习mysql,碰到了一个mysql stop fail的问题,在这里把碰到的问题以及解决的过程写出来,不是这个问题有多难,而是我在解决此问题的过程中没有发现一个行之有效的解决问题的中文网页,搞 ...

  5. [转][原]openstack-kilo--issue(六)kilo版openstack的dashboard在session超时后重新登录报错解决办法

    http://blog.csdn.net/wylfengyujiancheng/article/details/50523373?locationNum=1&fps=1 1.现象描述: kil ...

  6. mysql日志类型

    在MySQL中共有4中日志:错误日志.二进制日志.查询日志和慢查询日志 一.错误日志 错误日志名 host_name.err,并默认在参数DATADIR指定的目录中写入日志文件.可使用 --log-e ...

  7. [工具] 分布式系统下批量创建用户及分发公钥打通ssh通道的脚本

    在分布式系统下偶尔会有这样的需求,要增加一个服务时,需要在集群的所有节点上创建同一个用户,然后打通ssh通道,再分发软件包.配置.执行命令.启动服务... 这个问题的根源是如何集中配置和管理系统,专业 ...

  8. Proc-fs 编程

    一.简介 Proc文件系统是Linux系统上的一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更 ...

  9. windows 7系统搭建本地SVN服务器的过程

    Subversion是优秀的版本控制工具,其具体的的优点和详细介绍,这里就不再多说. 首先来下载和搭建SVN服务器. 现在Subversion已经迁移到apache网站上了,下载地址: http:// ...

  10. [转]ionic Accordion list three levels

    简化后的主要代码: $scope.groups = []; for (var i = 0; i < 2; i++) { $scope.groups[i] = { name: i, items: ...