• 信息安全系统设计基础实验五:通讯协议设计


    北京电子科技学院(BESTI)

    实验报告

    课程:信息安全系统设计基础 班级:1353

    姓名:芦畅 傅冬菁

    学号:20135308 20135311

    成绩: 指导教师:娄家鹏 实验日期:2015.11.24

    实验密级: 预习程度: 实验时间:15:30~18:00

    仪器组次: 必修/选修: 实验序号:5

    实验名称:实验五:通讯协议设计

    实验目的与要求:

    1、掌握在ARM开发板实现一个简单的WEB服务器的过程。
    2、学习在ARM开发板上的SOCKET网络编程。
    3、学习Linux下的signal()函数的使用。

    实验仪器:

    名称

    型号

    数量

    嵌入式开发平台

    UP-NETARM2410-CL

    1

    PC机

    DELL

    1

     实验内容、步骤与体会:

    1、阅读理解源码
    进入/arm2410cl/exp/basic/07_httpd 目录,使用vi 编辑器或其他编辑器阅读理解源代码。

    2、编译应用程序
    运行 make 产生可执行文件httpd,使用gcc编译器,手动编译07_httpd下的copy.c和httpd.c,再次查看文件夹时就可以发现已经生成httpd可执行文件。

    [root@zxt /]# cd /arm2410cl/exp/basic/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 下载到超级终端上,并拷贝测试用的网页进行调试,运行完之后是等待连接的界面,

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

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

    实验代码分析:

     / * 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 
     
    /*发送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;该语句不会执行到;
       
    } 

    实验过程中遇到的问题以及解决方案:

    (1)Make编译问题及解决方案

    问题:我们将07_httpd文件中全部拷贝进了bc中,文件夹中拥有Makefile文件,按照实验指导书中使用make编译时,出现错误,程序无法编译。

    解决:我们直接使用gcc手动单步编译的,与实验四相似,手动输入命令行,最终成功编译。

    实验总结:

    本次实验是我们在以实验一开发环境为背景的情况下最后一个实验,有了之前的基础,这次的比较简单易行。比起实验四面对make编译不成功时,我们的一筹莫展,现在我们掌握之前的经验方法,可以编译出来了,之后挂载部分与实验二相似,这次实验是简单嵌入式WEB服务器实验,通过对实验指导书的预习,我们明白了在ARM开发板上开发一个简单的WEB的过程,以及在ARM开发板上SOCKET网络编程和Linux下signal()函数的使用。我们通过这几次实验收获了很多实战经验,相信以后会用上它们。

  • 相关阅读:
    python:一个比较有趣的脚本
    opencv:图像模糊处理
    opencv:基本图形绘制
    opencv:摄像头和视频的读取
    C++:lambda表达式
    opencv:傅里叶变换
    opencv:创建滑动条
    opencv:通过滑动条调节亮度和对比度
    【源码】防抖和节流源码分析
    【css】最近使用的两种图标字体库
  • 原文地址:https://www.cnblogs.com/bonjourvivi/p/5014875.html
Copyright © 2020-2023  润新知