Linux网络编程一步一步学-自己编写一个HTTP协议的目录浏览和文件下载服务器

导读:
  From:http://www.chinaunix.net/jh/23/892147.html
  服务器源代码如下:
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #include
  #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 %d - %s"
  " Linux 下目录访问服务器


"
  "


", errno,   strerror(errno));   fprintf(client_sock,   " 请向管理员咨询为何出现如下错误提示:/n%s %s",
  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 %s"
  " Linux 下目录访问服务器


"
  "







", Path);   fprintf(client_sock,   "
目录 %s
/n",   Path);   fprintf(client_sock,   "
名称大小修改时间
/n");   if (dir == 0) {   fprintf(client_sock,   " %s",
  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, "");
  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,
  " (parent)",
  host, atoi(port) == 80 ? "" : nport,
  dir_up(Path));
  else
  fprintf(client_sock,
  " %s",
  host, atoi(port) == 80 ? "" : nport, Filename,
  dirent->d_name);
  if (S_ISDIR(info.st_mode))
  fprintf(client_sock, "目录");
  else if (S_ISREG(info.st_mode))
  fprintf(client_sock, "%d", info.st_size);
  else if (S_ISLNK(info.st_mode))
  fprintf(client_sock, "链接");
  else if (S_ISCHR(info.st_mode))
  fprintf(client_sock, "字符设备");
  else if (S_ISBLK(info.st_mode))
  fprintf(client_sock, "块设备");
  else if (S_ISFIFO(info.st_mode))
  fprintf(client_sock, "FIFO");
  else if (S_ISSOCK(info.st_mode))
  fprintf(client_sock, "Socket");
  else
  fprintf(client_sock, "(未知)");
  fprintf(client_sock, "%s", ctime(&info.st_ctime));
  }
  fprintf(client_sock, "/n");
  free(realFilename);
  }
  fprintf(client_sock, "
");
  } else {
  /* 既非常规文件又非目录,禁止访问 */
  fprintf(client_sock,
  "HTTP/1.1 200 OK/r/nServer: DAS by ZhouLifa/r/nConnection: close/r/n/r/n permission denied"
  " Linux 下目录访问服务器


"
  "

");   fprintf(client_sock,   " 你访问的资源'%s'被禁止访问,请联系管理员解决!",
  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的,因为程序是一次分配内存,会占用系统内存较大导致系统死掉!
  Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1934189

本文转自
http://blog.csdn.net/fatsandwich/archive/2007/12/14/1934189.aspx
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值