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

本文介绍了一个用C语言编写的简单目录访问服务器程序,该程序可在Linux环境下运行,支持基本的目录浏览和文件下载功能。文章提供了完整的源代码,并解释了如何编译和启动服务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章来源:http://bbs.chinaunix.net/viewthread.php?tid=892147&extra=page%3D3%26amp%3Bfilter%3Ddigest#

服务器源代码如下:
  1. #include <stdarg.h>
     
  2. #include <errno.h>
     
  3. #include <stdio.h>
     
  4. #include <fcntl.h>
     
  5. #include <unistd.h>
     
  6. #include <string.h>
     
  7. #include <time.h>
     
  8. #include <sys/types.h>
     
  9. #include <sys/stat.h>
     
  10. #include <dirent.h>
     
  11. #include <errno.h>
     
  12. #include <netinet/in.h>
     
  13. #include <sys/socket.h>
     
  14. #include <resolv.h>
     
  15. #include <arpa/inet.h>
     
  16. #include <stdlib.h>
     
  17. #include <signal.h>
     
  18. #include <getopt.h>
     

  19.  
  20. #define DEFAULTIP "127.0.0.1"
     
  21. #define DEFAULTPORT "80"
     
  22. #define DEFAULTBACK "10"
     
  23. #define DEFAULTDIR "/home"
     
  24. #define DEFAULTLOG "/tmp/das-server.log"
     

  25.  
  26. void prterrmsg(char *msg);
     
  27. #define prterrmsg(msg)        { perror(msg); abort(); }
     
  28. void wrterrmsg(char *msg);
     
  29. #define wrterrmsg(msg)        { fputs(msg, logfp); fputs(strerror(errno), logfp);fflush(logfp); abort(); }
     

  30.  
  31. void prtinfomsg(char *msg);
     
  32. #define prtinfomsg(msg)        { fputs(msg, stdout);  }
     
  33. void wrtinfomsg(char *msg);
     
  34. #define wrtinfomsg(msg)        {  fputs(msg, logfp); fflush(logfp);}
     

  35.  
  36. #define MAXBUF        1024
     

  37.  
  38. char buffer[MAXBUF + 1];
     
  39. char *host = 0;
     
  40. char *port = 0;
     
  41. char *back = 0;
     
  42. char *dirroot = 0;
     
  43. char *logdir = 0;
     
  44. unsigned char daemon_y_n = 0;
     
  45. FILE *logfp;
     

  46.  
  47. #define MAXPATH        150
     

  48.  
  49. /*----------------------------------------
     
  50. *--- dir_up - 查找dirpath所指目录的上一级目录
     
  51. *----------------------------------------
     
  52. */
     
  53. char *dir_up(char *dirpath)
     
  54. {
     
  55.     static char Path[MAXPATH];
     
  56.     int len;
     

  57.  
  58.     strcpy(Path, dirpath);
     
  59.     len = strlen(Path);
     
  60.     if (len > 1 && Path[len - 1] == '/')
     
  61.         len--;
     
  62.     while (Path[len - 1] != '/' && len > 1)
     
  63.         len--;
     
  64.     Path[len] = 0;
     
  65.     return Path;
     
  66. }
     

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

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

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

  116.  
  117.     /* 获得实际工作目录或文件的信息以判断是文件还是目录 */
     
  118.     if (stat(realPath, &info)) {
     
  119.         fprintf(client_sock,
     
  120.                 "HTTP/1.1 200 OK/r/nServer: DAS by ZhouLifa/r/nConnection: close/r/n/r/n<html><head><title>%d - %s</title></head>"
     
  121.                 "<body><font size=+4>Linux 下目录访问服务器</font><br><hr width=/"100%%/"><br><center>"
     
  122.                 "<table border cols=3 width=/"100%%/">", errno,
     
  123.                 strerror(errno));
     
  124.         fprintf(client_sock,
     
  125.                 "</table><font color=/"CC0000/" size=+2>请向管理员咨询为何出现如下错误提示:/n%s %s</font></body></html>",
     
  126.                 Path, strerror(errno));
     
  127.         goto out;
     
  128.     }
     
  129.     /* 处理浏览文件请求,即下载文件 */
     
  130.     if (S_ISREG(info.st_mode)) {
     
  131.         fd = open(realPath, O_RDONLY);
     
  132.         len = lseek(fd, 0, SEEK_END);
     
  133.         p = (char *) malloc(len + 1);
     
  134.         bzero(p, len + 1);
     
  135.         lseek(fd, 0, SEEK_SET);
     
  136.         ret = read(fd, p, len);
     
  137.         close(fd);
     
  138.         fprintf(client_sock,
     
  139.                 "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",
     
  140.                 len);
     
  141.         fwrite(p, len, 1, client_sock);
     
  142.         free(p);
     
  143.     } else if (S_ISDIR(info.st_mode)) {
     
  144.         /* 处理浏览目录请求 */
     
  145.         dir = opendir(realPath);
     
  146.         fprintf(client_sock,
     
  147.                 "HTTP/1.1 200 OK/r/nServer: DAS by ZhouLifa/r/nConnection: close/r/n/r/n<html><head><title>%s</title></head>"
     
  148.                 "<body><font size=+4>Linux 下目录访问服务器</font><br><hr width=/"100%%/"><br><center>"
     
  149.                 "<table border cols=3 width=/"100%%/">", Path);
     
  150.         fprintf(client_sock,
     
  151.                 "<caption><font size=+3>目录 %s</font></caption>/n",
     
  152.                 Path);
     
  153.         fprintf(client_sock,
     
  154.                 "<tr><td>名称</td><td>大小</td><td>修改时间</td></tr>/n");
     
  155.         if (dir == 0) {
     
  156.             fprintf(client_sock,
     
  157.                     "</table><font color=/"CC0000/" size=+2>%s</font></body></html>",
     
  158.                     strerror(errno));
     
  159.             return;
     
  160.         }
     
  161.         /* 读取目录里的所有内容 */
     
  162.         while ((dirent = readdir(dir)) != 0) {
     
  163.             if (strcmp(Path, "/") == 0)
     
  164.                 sprintf(Filename, "/%s", dirent->d_name);
     
  165.             else
     
  166.                 sprintf(Filename, "%s/%s", Path, dirent->d_name);
     
  167.             fprintf(client_sock, "<tr>");
     
  168.             len = strlen(dirroot) + strlen(Filename) + 1;
     
  169.             realFilename = malloc(len + 1);
     
  170.             bzero(realFilename, len + 1);
     
  171.             sprintf(realFilename, "%s/%s", dirroot, Filename);
     
  172.             if (stat(realFilename, &info) == 0) {
     
  173.                 if (strcmp(dirent->d_name, "..") == 0)
     
  174.                     fprintf(client_sock,
     
  175.                             "<td><a href=/"http://%s%s%s/">(parent)</a></td>",
     
  176.                             host, atoi(port) == 80 ? "" : nport,
     
  177.                             dir_up(Path));
     
  178.                 else
     
  179.                     fprintf(client_sock,
     
  180.                             "<td><a href=/"http://%s%s%s/">%s</a></td>",
     
  181.                             host, atoi(port) == 80 ? "" : nport, Filename,
     
  182.                             dirent->d_name);
     
  183.                 if (S_ISDIR(info.st_mode))
     
  184.                     fprintf(client_sock, "<td>目录</td>");
     
  185.                 else if (S_ISREG(info.st_mode))
     
  186.                     fprintf(client_sock, "<td>%d</td>", info.st_size);
     
  187.                 else if (S_ISLNK(info.st_mode))
     
  188.                     fprintf(client_sock, "<td>链接</td>");
     
  189.                 else if (S_ISCHR(info.st_mode))
     
  190.                     fprintf(client_sock, "<td>字符设备</td>");
     
  191.                 else if (S_ISBLK(info.st_mode))
     
  192.                     fprintf(client_sock, "<td>块设备</td>");
     
  193.                 else if (S_ISFIFO(info.st_mode))
     
  194.                     fprintf(client_sock, "<td>FIFO</td>");
     
  195.                 else if (S_ISSOCK(info.st_mode))
     
  196.                     fprintf(client_sock, "<td>Socket</td>");
     
  197.                 else
     
  198.                     fprintf(client_sock, "<td>(未知)</td>");
     
  199.                 fprintf(client_sock, "<td>%s</td>", ctime(&info.st_ctime));
     
  200.             }
     
  201.             fprintf(client_sock, "</tr>/n");
     
  202.             free(realFilename);
     
  203.         }
     
  204.         fprintf(client_sock, "</table></center></body></html>");
     
  205.     } else {
     
  206.         /* 既非常规文件又非目录,禁止访问 */
     
  207.         fprintf(client_sock,
     
  208.                 "HTTP/1.1 200 OK/r/nServer: DAS by ZhouLifa/r/nConnection: close/r/n/r/n<html><head><title>permission denied</title></head>"
     
  209.                 "<body><font size=+4>Linux 下目录访问服务器</font><br><hr width=/"100%%/"><br><center>"
     
  210.                 "<table border cols=3 width=/"100%%/">");
     
  211.         fprintf(client_sock,
     
  212.                 "</table><font color=/"CC0000/" size=+2>你访问的资源'%s'被禁止访问,请联系管理员解决!</font></body></html>",
     
  213.                 Path);
     
  214.     }
     
  215.   out:
     
  216.     free(realPath);
     
  217.     free(nport);
     
  218. }
     

  219.  
  220. /*------------------------------------------------------
     
  221. *--- getoption - 分析取出程序的参数
     
  222. *------------------------------------------------------
     
  223. */
     
  224. void getoption(int argc, char **argv)
     
  225. {
     
  226.     int c, len;
     
  227.     char *p = 0;
     

  228.  
  229.     opterr = 0;
     
  230.     while (1) {
     
  231.         int option_index = 0;
     
  232.         static struct option long_options[] = {
     
  233.             {"host", 1, 0, 0},
     
  234.             {"port", 1, 0, 0},
     
  235.             {"back", 1, 0, 0},
     
  236.             {"dir", 1, 0, 0},
     
  237.             {"log", 1, 0, 0},
     
  238.             {"daemon", 0, 0, 0},
     
  239.             {0, 0, 0, 0}
     
  240.         };
     
  241.         /* 本程序支持如一些参数:
     
  242.          * --host IP地址 或者 -H IP地址
     
  243.          * --port 端口 或者 -P 端口
     
  244.          * --back 监听数量 或者 -B 监听数量
     
  245.          * --dir 网站根目录 或者 -D 网站根目录
     
  246.          * --log 日志存放路径 或者 -L 日志存放路径
     
  247.          * --daemon 使程序进入后台运行模式
     
  248.          */
     
  249.         c = getopt_long(argc, argv, "H:P:B:D:L",
     
  250.                         long_options, &option_index);
     
  251.         if (c == -1 || c == '?')
     
  252.             break;
     

  253.  
  254.         if(optarg)        len = strlen(optarg);
     
  255.         else        len = 0;
     

  256.  
  257.         if ((!c && !(strcasecmp(long_options[option_index].name, "host")))
     
  258.             || c == 'H')
     
  259.             p = host = malloc(len + 1);
     
  260.         else if ((!c
     
  261.                   &&
     
  262.                   !(strcasecmp(long_options[option_index].name, "port")))
     
  263.                  || c == 'P')
     
  264.             p = port = malloc(len + 1);
     
  265.         else if ((!c
     
  266.                   &&
     
  267.                   !(strcasecmp(long_options[option_index].name, "back")))
     
  268.                  || c == 'B')
     
  269.             p = back = malloc(len + 1);
     
  270.         else if ((!c
     
  271.                   && !(strcasecmp(long_options[option_index].name, "dir")))
     
  272.                  || c == 'D')
     
  273.             p = dirroot = malloc(len + 1);
     
  274.         else if ((!c
     
  275.                   && !(strcasecmp(long_options[option_index].name, "log")))
     
  276.                  || c == 'L')
     
  277.             p = logdir = malloc(len + 1);
     
  278.         else if ((!c
     
  279.                   &&
     
  280.                   !(strcasecmp
     
  281.                     (long_options[option_index].name, "daemon")))) {
     
  282.             daemon_y_n = 1;
     
  283.             continue;
     
  284.         }
     
  285.         else
     
  286.             break;
     
  287.         bzero(p, len + 1);
     
  288.         memcpy(p, optarg, len);
     
  289.     }
     
  290. }
     

  291.  
  292. int main(int argc, char **argv)
     
  293. {
     
  294.     struct sockaddr_in addr;
     
  295.     int sock_fd, addrlen;
     

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

  299.  
  300.     if (!host) {
     
  301.         addrlen = strlen(DEFAULTIP);
     
  302.         AllocateMemory(&host, addrlen, DEFAULTIP);
     
  303.     }
     
  304.     if (!port) {
     
  305.         addrlen = strlen(DEFAULTPORT);
     
  306.         AllocateMemory(&port, addrlen, DEFAULTPORT);
     
  307.     }
     
  308.     if (!back) {
     
  309.         addrlen = strlen(DEFAULTBACK);
     
  310.         AllocateMemory(&back, addrlen, DEFAULTBACK);
     
  311.     }
     
  312.     if (!dirroot) {
     
  313.         addrlen = strlen(DEFAULTDIR);
     
  314.         AllocateMemory(&dirroot, addrlen, DEFAULTDIR);
     
  315.     }
     
  316.     if (!logdir) {
     
  317.         addrlen = strlen(DEFAULTLOG);
     
  318.         AllocateMemory(&logdir, addrlen, DEFAULTLOG);
     
  319.     }
     

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

  324.  
  325.     /* fork() 两次处于后台工作模式下 */
     
  326.     if (daemon_y_n) {
     
  327.         if (fork())
     
  328.             exit(0);
     
  329.         if (fork())
     
  330.             exit(0);
     
  331.         close(0), close(1), close(2);
     
  332.         logfp = fopen(logdir, "a+");
     
  333.         if (!logfp)
     
  334.             exit(0);
     
  335.     }
     

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

  339.  
  340.     /* 创建 socket */
     
  341.     if ((sock_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
     
  342.         if (!daemon_y_n) {
     
  343.             prterrmsg("socket()");
     
  344.         } else {
     
  345.             wrterrmsg("socket()");
     
  346.         }
     
  347.     }
     

  348.  
  349.     /* 设置端口快速重用 */
     
  350.     addrlen = 1;
     
  351.     setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &addrlen,
     
  352.                sizeof(addrlen));
     

  353.  
  354.     addr.sin_family = AF_INET;
     
  355.     addr.sin_port = htons(atoi(port));
     
  356.     addr.sin_addr.s_addr = inet_addr(host);
     
  357.     addrlen = sizeof(struct sockaddr_in);
     
  358.     /* 绑定地址、端口等信息 */
     
  359.     if (bind(sock_fd, (struct sockaddr *) &addr, addrlen) < 0) {
     
  360.         if (!daemon_y_n) {
     
  361.             prterrmsg("bind()");
     
  362.         } else {
     
  363.             wrterrmsg("bind()");
     
  364.         }
     
  365.     }
     

  366.  
  367.     /* 开启临听 */
     
  368.     if (listen(sock_fd, atoi(back)) < 0) {
     
  369.         if (!daemon_y_n) {
     
  370.             prterrmsg("listen()");
     
  371.         } else {
     
  372.             wrterrmsg("listen()");
     
  373.         }
     
  374.     }
     
  375.     while (1) {
     
  376.         int len;
     
  377.         int new_fd;
     
  378.         addrlen = sizeof(struct sockaddr_in);
     
  379.         /* 接受新连接请求 */
     
  380.         new_fd = accept(sock_fd, (struct sockaddr *) &addr, &addrlen);
     
  381.         if (new_fd < 0) {
     
  382.             if (!daemon_y_n) {
     
  383.                 prterrmsg("accept()");
     
  384.             } else {
     
  385.                 wrterrmsg("accept()");
     
  386.             }
     
  387.             break;
     
  388.         }
     
  389.         bzero(buffer, MAXBUF + 1);
     
  390.         sprintf(buffer, "连接来自于: %s:%d/n",
     
  391.                 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
     
  392.         if (!daemon_y_n) {
     
  393.             prtinfomsg(buffer);
     
  394.         } else {
     
  395.             wrtinfomsg(buffer);
     
  396.         }
     
  397.         /* 产生一个子进程去处理请求,当前进程继续等待新的连接到来 */
     
  398.         if (!fork()) {
     
  399.             bzero(buffer, MAXBUF + 1);
     
  400.             if ((len = recv(new_fd, buffer, MAXBUF, 0)) > 0) {
     
  401.                 FILE *ClientFP = fdopen(new_fd, "w");
     
  402.                 if (ClientFP == NULL) {
     
  403.                     if (!daemon_y_n) {
     
  404.                         prterrmsg("fdopen()");
     
  405.                     } else {
     
  406.                         prterrmsg("fdopen()");
     
  407.                     }
     
  408.                 } else {
     
  409.                     char Req[MAXPATH + 1] = "";
     
  410.                     sscanf(buffer, "GET %s HTTP", Req);
     
  411.                     bzero(buffer, MAXBUF + 1);
     
  412.                     sprintf(buffer, "请求取文件: /"%s/"/n", Req);
     
  413.                     if (!daemon_y_n) {
     
  414.                         prtinfomsg(buffer);
     
  415.                     } else {
     
  416.                         wrtinfomsg(buffer);
     
  417.                     }
     
  418.                     /* 处理用户请求 */
     
  419.                     GiveResponse(ClientFP, Req);
     
  420.                     fclose(ClientFP);
     
  421.                 }
     
  422.             }
     
  423.             exit(0);
     
  424.         }
     
  425.         close(new_fd);
     
  426.     }
     
  427.     close(sock_fd);
     
  428.     return 0;
     
  429. }
复制代码

编译程序用下列命令:
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的,因为程序是一次分配内存,会占用系统内存较大导致系统死掉!
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值