查看: 7866|回复: 0
打印 上一主题 下一主题

用C语言编写一个HTTP协议的目录浏览和文件下载服务器

[复制链接]
跳转到指定楼层
1#
发表于 2007-9-9 23:05:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
台州网址导航
#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <signal.h>
#include <getopt.h>

#define DEFAULTIP "127.0.0.1"
#define DEFAULTPORT "80"
#define DEFAULTBACK "10"
#define DEFAULTDIR "/home"
#define DEFAULTLOG "/tmp/das-server.log"

void prterrmsg(char *msg);
#define prterrmsg(msg)     { perror(msg); abort(); }
void wrterrmsg(char *msg);
#define wrterrmsg(msg)     { fputs(msg, logfp); fputs(strerror(errno), logfp);fflush(logfp); abort(); }

void prtinfomsg(char *msg);
#define prtinfomsg(msg)     { fputs(msg, stdout); }
void wrtinfomsg(char *msg);
#define wrtinfomsg(msg)     { fputs(msg, logfp); fflush(logfp);}

#define MAXBUF     1024

char buffer[MAXBUF + 1];
char *host = 0;
char *port = 0;
char *back = 0;
char *dirroot = 0;
char *logdir = 0;
unsigned char daemon_y_n = 0;
FILE *logfp;

#define MAXPATH     150

/*----------------------------------------
*--- dir_up - 查找dirpath所指目录的上一级目录
*----------------------------------------
*/
char *dir_up(char *dirpath)
{
  static char Path[MAXPATH];
  int len;

  strcpy(Path, dirpath);
  len = strlen(Path);
  if (len > 1 && Path[len - 1] == '/')
    len--;
  while (Path[len - 1] != '/' && len > 1)
    len--;
  Path[len] = 0;
  return Path;
}

/*------------------------------------------------------
*--- AllocateMemory - 分配空间并把d所指的内容复制
*------------------------------------------------------
*/
void AllocateMemory(char **s, int l, char *d)
{
  *s = malloc(l + 1);
  bzero(*s, l + 1);
  memcpy(*s, d, l);
}
/************关于本文档********************************************
*filename: das-server.c
*purpose: 这是在Linux下用C语言写的目录访问服务器,支持目录浏览和文件下载
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-26 19:32
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
/*------------------------------------------------------
*--- GiveResponse - 把Path所指的内容发送到client_sock去
*-------------------如果Path是一个目录,则列出目录内容
*-------------------如果Path是一个文件,则下载文件
*------------------------------------------------------
*/
void GiveResponse(FILE * client_sock, char *Path)
{
  struct dirent *dirent;
  struct stat info;
  char Filename[MAXPATH];
  DIR *dir;
  int fd, len, ret;
  char *p, *realPath, *realFilename, *nport;

  /* 获得实际工作目录或文件 */
  len = strlen(dirroot) + strlen(Path) + 1;
  realPath = malloc(len + 1);
  bzero(realPath, len + 1);
  sprintf(realPath, "%s/%s", dirroot, Path);

  /* 获得实际工作端口 */
  len = strlen(port) + 1;
  nport = malloc(len + 1);
  bzero(nport, len + 1);
  sprintf(nport, ":%s", port);

  /* 获得实际工作目录或文件的信息以判断是文件还是目录 */
  if (stat(realPath, &info)) {
    fprintf(client_sock,
          "HTTP/1.1 200 OK\r\nServer: DAS by ZhouLifa\r\nConnection: close\r\n\r\n<html><head><title>%d - %s</title></head>"
          "<body><font size=+4>Linux 下目录访问服务器</font><br><hr width=\"100%%\"><br><center>"
          "<table border cols=3 width=\"100%%\">", errno,
          strerror(errno));
    fprintf(client_sock,
          "</table><font color=\"CC0000\" size=+2>请向管理员咨询为何出现如下错误提示:\n%s %s</font></body></html>",
          Path, strerror(errno));
    goto out;
  }
  /* 处理浏览文件请求,即下载文件 */
  if (S_ISREG(info.st_mode)) {
    fd = open(realPath, O_RDONLY);
    len = lseek(fd, 0, SEEK_END);
    p = (char *) malloc(len + 1);
    bzero(p, len + 1);
    lseek(fd, 0, SEEK_SET);
    ret = read(fd, p, len);
    close(fd);
    fprintf(client_sock,
          "HTTP/1.1 200 OK\r\nServer: DAS by ZhouLifa\r\nConnection: keep-alive\r\nContent-type: application/*\r\nContent-Length:%d\r\n\r\n",
          len);
    fwrite(p, len, 1, client_sock);
    free(p);
  } else if (S_ISDIR(info.st_mode)) {
    /* 处理浏览目录请求 */
    dir = opendir(realPath);
    fprintf(client_sock,
          "HTTP/1.1 200 OK\r\nServer: DAS by ZhouLifa\r\nConnection: close\r\n\r\n<html><head><title>%s</title></head>"
          "<body><font size=+4>Linux 下目录访问服务器</font><br><hr width=\"100%%\"><br><center>"
          "<table border cols=3 width=\"100%%\">", Path);
    fprintf(client_sock,
          "<caption><font size=+3>目录 %s</font></caption>\n",
          Path);
    fprintf(client_sock,
          "<tr><td>名称</td><td>大小</td><td>修改时间</td></tr>\n");
    if (dir == 0) {
        fprintf(client_sock,
            "</table><font color=\"CC0000\" size=+2>%s</font></body></html>",
            strerror(errno));
        return;
    }
    /* 读取目录里的所有内容 */
    while ((dirent = readdir(dir)) != 0) {
        if (strcmp(Path, "/") == 0)
          sprintf(Filename, "/%s", dirent->d_name);
        else
          sprintf(Filename, "%s/%s", Path, dirent->d_name);
        fprintf(client_sock, "<tr>");
        len = strlen(dirroot) + strlen(Filename) + 1;
        realFilename = malloc(len + 1);
        bzero(realFilename, len + 1);
        sprintf(realFilename, "%s/%s", dirroot, Filename);
        if (stat(realFilename, &info) == 0) {
          if (strcmp(dirent->d_name, "..") == 0)
            fprintf(client_sock,
                  "<td><a href=\"http://%s%s%s\">(parent)</a></td>",
                  host, atoi(port) == 80 ? "" : nport,
                  dir_up(Path));
          else
            fprintf(client_sock,
                  "<td><a href=\"http://%s%s%s\">%s</a></td>",
                  host, atoi(port) == 80 ? "" : nport, Filename,
                  dirent->d_name);
          if (S_ISDIR(info.st_mode))
            fprintf(client_sock, "<td>目录</td>");
          else if (S_ISREG(info.st_mode))
            fprintf(client_sock, "<td>%d</td>", info.st_size);
          else if (S_ISLNK(info.st_mode))
            fprintf(client_sock, "<td>链接</td>");
          else if (S_ISCHR(info.st_mode))
            fprintf(client_sock, "<td>字符设备</td>");
          else if (S_ISBLK(info.st_mode))
            fprintf(client_sock, "<td>块设备</td>");
          else if (S_ISFIFO(info.st_mode))
            fprintf(client_sock, "<td>FIFO</td>");
          else if (S_ISSOCK(info.st_mode))
            fprintf(client_sock, "<td>Socket</td>");
          else
            fprintf(client_sock, "<td>(未知)</td>");
          fprintf(client_sock, "<td>%s</td>", ctime(&info.st_ctime));
        }
        fprintf(client_sock, "</tr>\n");
        free(realFilename);
    }
    fprintf(client_sock, "</table></center></body></html>");
  } else {
    /* 既非常规文件又非目录,禁止访问 */
    fprintf(client_sock,
          "HTTP/1.1 200 OK\r\nServer: DAS by ZhouLifa\r\nConnection: close\r\n\r\n<html><head><title>permission denied</title></head>"
          "<body><font size=+4>Linux 下目录访问服务器</font><br><hr width=\"100%%\"><br><center>"
          "<table border cols=3 width=\"100%%\">");
    fprintf(client_sock,
          "</table><font color=\"CC0000\" size=+2>你访问的资源'%s'被禁止访问,请联系管理员解决!</font></body></html>",
          Path);
  }
out:
  free(realPath);
  free(nport);
}

/*------------------------------------------------------
*--- getoption - 分析取出程序的参数
*------------------------------------------------------
*/
void getoption(int argc, char **argv)
{
  int c, len;
  char *p = 0;

  opterr = 0;
  while (1) {
    int option_index = 0;
    static struct option long_options[] = {
        {"host", 1, 0, 0},
        {"port", 1, 0, 0},
        {"back", 1, 0, 0},
        {"dir", 1, 0, 0},
        {"log", 1, 0, 0},
        {"daemon", 0, 0, 0},
        {0, 0, 0, 0}
    };
    /* 本程序支持如一些参数:
      * --host IP地址 或者 -H IP地址
      * --port 端口 或者 -P 端口
      * --back 监听数量 或者 -B 监听数量
      * --dir 网站根目录 或者 -D 网站根目录
      * --log 日志存放路径 或者 -L 日志存放路径
      * --daemon 使程序进入后台运行模式
      */
    c = getopt_long(argc, argv, "H:P:B:D:L",
                long_options, &option_index);
    if (c == -1 || c == '?')
        break;

    if(optarg)     len = strlen(optarg);
    else     len = 0;

    if ((!c && !(strcasecmp(long_options[option_index].name, "host")))
        || c == 'H')
        p = host = malloc(len + 1);
    else if ((!c
            &&
            !(strcasecmp(long_options[option_index].name, "port")))
          || c == 'P')
        p = port = malloc(len + 1);
    else if ((!c
            &&
            !(strcasecmp(long_options[option_index].name, "back")))
          || c == 'B')
        p = back = malloc(len + 1);
    else if ((!c
            && !(strcasecmp(long_options[option_index].name, "dir")))
          || c == 'D')
        p = dirroot = malloc(len + 1);
    else if ((!c
            && !(strcasecmp(long_options[option_index].name, "log")))
          || c == 'L')
        p = logdir = malloc(len + 1);
    else if ((!c
            &&
            !(strcasecmp
            (long_options[option_index].name, "daemon")))) {
        daemon_y_n = 1;
        continue;
    }
    else
        break;
    bzero(p, len + 1);
    memcpy(p, optarg, len);
  }
}

int main(int argc, char **argv)
{
  struct sockaddr_in addr;
  int sock_fd, addrlen;

  /* 获得程序工作的参数,如 IP 、端口、监听数、网页根目录、目录存放位置等 */
  getoption(argc, argv);

  if (!host) {
    addrlen = strlen(DEFAULTIP);
    AllocateMemory(&host, addrlen, DEFAULTIP);
  }
  if (!port) {
    addrlen = strlen(DEFAULTPORT);
    AllocateMemory(&port, addrlen, DEFAULTPORT);
  }
  if (!back) {
    addrlen = strlen(DEFAULTBACK);
    AllocateMemory(&back, addrlen, DEFAULTBACK);
  }
  if (!dirroot) {
    addrlen = strlen(DEFAULTDIR);
    AllocateMemory(&dirroot, addrlen, DEFAULTDIR);
  }
  if (!logdir) {
    addrlen = strlen(DEFAULTLOG);
    AllocateMemory(&logdir, addrlen, DEFAULTLOG);
  }

  printf
    ("host=%s port=%s back=%s dirroot=%s logdir=%s %s是后台工作模式(进程ID:%d)\n",
      host, port, back, dirroot, logdir, daemon_y_n?"":"不", getpid());

  /* fork() 两次处于后台工作模式下 */
  if (daemon_y_n) {
    if (fork())
        exit(0);
    if (fork())
        exit(0);
    close(0), close(1), close(2);
    logfp = fopen(logdir, "a+");
    if (!logfp)
        exit(0);
  }

  /* 处理子进程退出以免产生僵尸进程 */
  signal(SIGCHLD, SIG_IGN);

  /* 创建 socket */
  if ((sock_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
    if (!daemon_y_n) {
        prterrmsg("socket()");
    } else {
        wrterrmsg("socket()");
    }
  }

  /* 设置端口快速重用 */
  addrlen = 1;
  setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &addrlen,
          sizeof(addrlen));

  addr.sin_family = AF_INET;
  addr.sin_port = htons(atoi(port));
  addr.sin_addr.s_addr = inet_addr(host);
  addrlen = sizeof(struct sockaddr_in);
  /* 绑定地址、端口等信息 */
  if (bind(sock_fd, (struct sockaddr *) &addr, addrlen) < 0) {
    if (!daemon_y_n) {
        prterrmsg("bind()");
    } else {
        wrterrmsg("bind()");
    }
  }

  /* 开启临听 */
  if (listen(sock_fd, atoi(back)) < 0) {
    if (!daemon_y_n) {
        prterrmsg("listen()");
    } else {
        wrterrmsg("listen()");
    }
  }
  while (1) {
    int len;
    int new_fd;
    addrlen = sizeof(struct sockaddr_in);
    /* 接受新连接请求 */
    new_fd = accept(sock_fd, (struct sockaddr *) &addr, &addrlen);
    if (new_fd < 0) {
        if (!daemon_y_n) {
          prterrmsg("accept()");
        } else {
          wrterrmsg("accept()");
        }
        break;
    }
    bzero(buffer, MAXBUF + 1);
    sprintf(buffer, "连接来自于: %s:%d\n",
          inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
    if (!daemon_y_n) {
        prtinfomsg(buffer);
    } else {
        wrtinfomsg(buffer);
    }
    /* 产生一个子进程去处理请求,当前进程继续等待新的连接到来 */
    if (!fork()) {
        bzero(buffer, MAXBUF + 1);
        if ((len = recv(new_fd, buffer, MAXBUF, 0)) > 0) {
          FILE *ClientFP = fdopen(new_fd, "w");
          if (ClientFP == NULL) {
            if (!daemon_y_n) {
                prterrmsg("fdopen()");
            } else {
                prterrmsg("fdopen()");
            }
          } else {
            char Req[MAXPATH + 1] = "";
            sscanf(buffer, "GET %s HTTP", Req);
            bzero(buffer, MAXBUF + 1);
            sprintf(buffer, "请求取文件: \"%s\"\n", Req);
            if (!daemon_y_n) {
                prtinfomsg(buffer);
            } else {
                wrtinfomsg(buffer);
            }
            /* 处理用户请求 */
            GiveResponse(ClientFP, Req);
            fclose(ClientFP);
          }
        }
        exit(0);
    }
    close(new_fd);
  }
  close(sock_fd);
  return 0;
}
编译程序用下列命令:
gcc -Wall das-server.c -o das-server
注:das即 Dictory Access Server
以root用户启动服务程序用下列命令:
./das-server
或以普通用户启动服务程序用下列命令:
./das-server --port 7838

./das-server -P 7838
注:只有root用户才有权限启动1024以下的端口,所以如果想用默认的80端口就得用root来运行。
如果要想让程序在后台自动运行,即处理精灵模式下工作,在命令后面加上--daemon参数即可。
打开一个网络浏览器输入服务地址开始浏览,如下图:



下载文件如下图:



注:请不要下载较大的文件,比如文件大小超过10M的,因为程序是一次分配内存,会占用系统内存较大导致系统死掉!
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 转播转播 分享分享 分享淘帖
台州维博网络(www.tzweb.com)专门运用PHP+MYSQL/ASP.NET+MSSQL技术开发网站门户平台系统等。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

网站推广
关于我们
  • 台州朗动科技(Tzweb.com)拥有多年开发网站平台系统门户手机客户端等业务的成功经验。主要从事:政企网站,系统平台,微信公众号,各类小程序,手机APP客户端,浙里办微应用,浙政钉微应用、主机域名、虚拟空间、后期维护等服务,满足不同企业公司的需求,是台州地区领先的网络技术服务商!

Hi,扫描关注我

Copyright © 2005-2026 站长论坛 All rights reserved

Powered by 站长论坛 with TZWEB Update Techonolgy Support

快速回复 返回顶部 返回列表