zoukankan      html  css  js  c++  java
  • 20145327 《信息安全系统设计基础》 实验五 网络通信

    20145327 《信息安全系统设计基础》 实验五 简单嵌入式WEB服务器实验

    结伴20145305解佳玲博客链接

    实验步骤(截图见结伴小伙伴博客)

    1 进入/arm2410cl/exp/basic/07_httpd 目录,使用 vi 编辑器或其他编辑器阅读理解源代码
    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;该语句不会执行到;
       
    } 
    

    2 编译应用程序 运行 make 产生可执行文件 httpd

    [root@zxt /]# cd /home/bc/ws/07_httpd/
    [root@zxt 07_httpd]# make
    armv4l-unknown-linux-gcc -DHTTPD_DOCUMENT_ROOT="/mnt/yaffs" -c -o httpd.o httpd.c
    armv4l-unknown-linux-gcc -DHTTPD_DOCUMENT_ROOT="/mnt/yaffs" -c -o copy.c
    armv4l-unknown-linux-gcc -o ../bin/httpd httpd.o copy.o -lpthread
    armv4l-unknown-linux-gcc -o httpd httpd.o copy.o -lpthread
    [root@zxt 07_httpd]# ls
    copy.c doc httpd httpd.o Makefile
    copy.o Google httpd.c index.html Makefile.bak
    3 下载调试 使用 NFS 服务方式将 HTTPD 下载到开发板上,并拷贝测试用的网

    页进行调试,本例中用的是 index 测试网页。

    [/mnt/yaffs] mount -t nfs -o nolock 192.168.0.56:/arm2410cl /host
    [/mnt/yaffs]cd /bc/ws/07_httpd/
    [/bc/ws/07_httpd]./httpd
    starting httpd...
    press q to quit.
    wait for connection.

    4 在台式机的IE浏览器中输入 http://192.168.0.123 可见实验结果

    实验体会

    Make——工程管理器,为了减少重复工作量,“自动编译管理器”,“自动”在于它能根据文件时间戳自动发现更新过的文件而减少编译工作量,同时它通过读入makefile文件的内容来执行大量的编译工作。用户只需要编写一次简答的编译语句就可以了,也就是说以后只要敲入make即可编译全部文件,它大大提高了实际项目的工作效率,几乎所有linux下的项目编程都需要用到它。make真的很好用,前提是弄好Mikefile,还有Path的路径设置。

  • 相关阅读:
    Linux下sed,awk,grep,cut,find学习笔记
    Python文件处理(1)
    KMP详解
    Java引用详解
    解决安卓中页脚被输入法顶起的问题
    解决swfupload上传控件文件名中文乱码问题 三种方法 flash及最新版本11.8.800.168
    null id in entry (don't flush the Session after an exception occurs)
    HQL中的Like查询需要注意的地方
    spring mvc controller间跳转 重定向 传参
    node to traverse cannot be null!
  • 原文地址:https://www.cnblogs.com/20145327gc/p/6052129.html
Copyright © 2011-2022 走看看