Chinaunix首页 | 论坛 | 博客
  • 博客访问: 55182
  • 博文数量: 12
  • 博客积分: 207
  • 博客等级: 入伍新兵
  • 技术积分: 115
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-25 11:00
文章分类
文章存档

2013年(4)

2012年(8)

分类: LINUX

2012-10-31 18:28:56

 httpd.c代码分析.rar  



/* httpd.c:  A very simple http server
 * Copyfight (C) 2003      Zou jian guo
 * Copyright (C) 2000         Lineo, Inc.  ()
 * Copyright (c) 1997-1999 D. Jeff Dionne
 * Copyright (c) 1998      Kenneth Albanowski
 * Copyright (c) 1999      Nick Brok
 *
 * 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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 OK\n"); //服务器回应http协议数据头的状态行;发送请求成功;
  switch (content_type)
  {  
   case 't':
    fprintf(f,"Content-type: text/plain\n"); //发送纯文本文件信息;
    break;
   case 'g':
    fprintf(f,"Content-type: image/gif\n"); //发送gif格式图片信息;
    break;
   case 'j':
    fprintf(f,"Content-type: image/jpeg\n"); //发送gpeg格式图片信息;
    break;
   case 'h':
    fprintf(f,"Content-type: text/html\n"); //发送html信息;
    break;
  }
  fprintf(f,"Server: uClinux-httpd 0.2.2\n"); //发送服务器版本信息;
  fprintf(f,"Expires: 0\n"); //发送文件永不过期信息;
  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, %d\n", 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, %d\n", 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, %d\n", name, errno);
    fflush(f);
    return -1;
  }
   
  PrintHeader(f,'h'); //发送h类型的http协议数据头信息
   
  alarm(TIMEOUT);
  fprintf(f, "

Index of %s

\n\n",name);
  alarm(0);
 
  if (name[strlen(name)-1] != '/') { //若名字的后面没有/则默认加上 /;
    strcat(name, "/");
  }
   
  while(dirent = readdir(dir)) { //读取目录;
    alarm(TIMEOUT);
   
    fprintf(f, "

%s

\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, %d\n", 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, %d\n", 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.1\r\n
#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.1\r\n
   
      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所在位置处的字符设为\0; \0的ASCII码值是0
          arg = r+1; //arg指向下一个参数;
          if (e = strchr(arg,' '))  
        {
              *e = '\0';  //如果arg为空则将arg所在位置置为\0复制给e;
          }
      } else  
    {     // 如果当前r指向字符不为 '?', 将r指向字符置为 '\0',  
          arg = 0;  
          *r = 0;   // r处设为\0;
    }
   
      c = bp;//将bp赋值给c;
 
/*zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz*/     
      if (c[0] == 0x20){ //判断c中的字符内容是否为空格;若为空格
        c[0]='.'; //将.和\0放入c数组中;
        c[1]='\0';  
    }
/*zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz*/     
    if(c[0] == '\0') strcat(c,"."); //若 c中为\0则将.链接在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后,后面追加\0;
            if (!stat(c, &stbuf)) //通过文件名c获取文件信息,并保存在stbuf中 ;成功返回0;
            {
                DoHTML(f, c); //对html文件进行处理;
            }  
            else  
            {
                  *end = '\0'; //将end指向\0;
                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, "404 File Not Found\n"); //打印出错信息
        fprintf(f, "The requested URL was not found on this server \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 %d\n", 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 %d\n", errno);
    fclose(f); //关闭文件描述符;
    alarm(0);   
    return 0;
  }
#ifdef DEBUG
      printf("buf = '%s'\n", buf); //打印客户机发出的请求命令;
#endif
     
      alarm(0);  //将闹钟时间清0;
 
      referrer[0] = '\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 %d\n", 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;该语句不会执行到;
  
}
阅读(3127) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

最大行业软件2012-12-30 11:08:52

ANSA.v14.0.Pre.16.11.2012.Win32_64 1CD

Beta-CAE MetaPost 6.8.2 Win32_64 1CD

CMG Suite v2012.0 Win32-ISO 1DVD

CMG Suite v2012.0 Win64-ISO 1DVD

Delcam.PowerShape.2013.SP6.Update.Only.Win32_64 2CD

ESI PAM-Stamp 2G 2012.0 Linux32_64 1DVD

Surpac v6.3.2 Win32 1CD

Topcon.Tools.v8.2 1CD

Embird Plus v8.0 1CD

Keil RealView Microcontroller Development Kit 4.60 1CD