zoukankan      html  css  js  c++  java
  • C语言实现的Web服务器(转-kungstriving)

    自己研究了好几天终于写出来一个,哈哈,当然也从网上得到了很多的帮助拉。谢谢大家咯!这个版本还不是很完善,但Web服务器的基本框架已经出来了,还有部分的功能需要进行进一步的测试和修改。虽然说C的开发比较慢,对于程序员来说比较难以操作,但通过用C写这些很底层的东西,可以更好的了解的象java的socket中的工作原理。有一定的帮助!
     
    以下是源代码:
     
    /**************filename: Server.cpp****************
     该程序通过标准socket实现简单Http服务器
     运行该服务器可以通过浏览器访问服务器目录下的
     Html文件和jpg图片 完成初步的Http服务器功能
    ***************************************************/
    #include <winsock.h>
    #include <sys/stat.h>
    #include <iostream>
    using namespace std;
    #define SERVER_PORT 10000         //自定义的服务端口
    #define HOSTLEN 256           //主机名长度
    #define BACKLOG 10           //同时等待的连接个数
    /**************************************
     该方法包装了send()
     通过该方法发送数据 能够全部发出
     没有遗漏
    **************************************/
    int sendall(int s, char *buf, int *len) {
     int total = 0;           // 已经发送字节数
     int bytesleft = *len;                                   //还剩余多少字节
     int n;
     while(total < *len) {
      n = send(s, buf+total, bytesleft, 0);
      if (n == -1) { break; }
      total += n;
      bytesleft -= n;
     }
     *len = total;           // 返回实际发送出去的字节数
     return n==-1?-1:0;          // 成功发送返回0 失败-1
    }
    /**************************************
     该方法处理错误请求
     并向客户端发送错误信息
    **************************************/
    void wrong_req(int sock) {
     char* error_head = "HTTP/1.0 501 Not Implemented "; //输出501错误
     int len = strlen(error_head);
     if (sendall(sock, error_head, &len) == -1) {   //向客户发送
      printf("Sending failed!");
      return;
     }      
     char* error_type = "Content-type: text/plain ";  
     len = strlen(error_type);
     if (sendall(sock, error_type, &len) == -1) {
      printf("Sending failed!");
      return;
     }
     char* error_end = " ";
     len = strlen(error_end);
     if (sendall(sock, error_end, &len) == -1) {
      printf("Sending failed!");
      return;
     }
     char* prompt_info = "The command is not yet completed ";
     len = strlen(prompt_info);
     if (sendall(sock, prompt_info, &len) == -1) {
      printf("Sending failed!");
      return;
     }
    }
    /**********************************
     该方法判断用户请求的文件是否存在
     不存在返回true 存在返回false
    ***********************************/
    bool not_exit(char* arguments) {
     struct stat dir_info;
     return (stat(arguments, &dir_info) == -1);
    }
    /*************************************
     所请求的文件不存在
    *************************************/
    void file_not_found(char* arguments, int sock) {
     char* error_head = "HTTP/1.0 404 Not Found ";   //构造404错误head
     int len = strlen(error_head);
     if (sendall(sock, error_head, &len) == -1) {    //向客户端发送
      printf("Sending error!");
      return;
     }   
     char* error_type = "Content-type: text/plain ";
     len = strlen(error_type);
     if (sendall(sock, error_type, &len) == -1) {
      printf("Sending error!");
      return;
     }
     char* error_end = " ";
     len = strlen(error_end);
     if (sendall(sock, error_end, &len) == -1) {
      printf("Sending error!");
      return;
     }
     char prompt_info[50] = "Not found:  ";
     strcat(prompt_info, arguments);
     len = strlen(prompt_info);
     if (sendall(sock, prompt_info, &len) == -1) {    //输出未找到的文件
      printf("Sending error!");
      return;
     }    
    }
    /*************************************
     发送Http协议头部信息
     其中包括响应类型和Content Type
    *************************************/
    void send_header(int send_to, char* content_type) {
     
     char* head = "HTTP/1.0 200 OK ";     //正确的头部信息
     int len = strlen(head);
     if (sendall(send_to, head, &len) == -1) {   //向连接的客户端发送数据
      printf("Sending error");
      return;
     }
     if (content_type) {         //content_type不为空
      char temp_1[30] = "Content-type: ";    //准备好要连接的字串
      strcat(temp_1, content_type);     //构造content_type
      strcat(temp_1, " ");
      len = strlen(temp_1);
      if (sendall(send_to, temp_1, &len) == -1) {
       printf("Sending error!");
       return;
      }
     }
    }
    /***********************************
     取得用户所请求的文件类型
     即文件后缀 (.html .jpg .gif)
    ************************************/
    char* file_type(char* arg) {
     char * temp;          //临时字符串指针
     if ((temp=strrchr(arg,'.')) != NULL) {    //取得后缀
      return temp+1;
     }
     return "";           //如果请求的文件名中没有. 则返回空串
    }
    /*************************************
     该方法为程序核心
     负责真正发送文件 如*.html *.jpg等
    *************************************/
    void send_file(char* arguments, int sock) {
     char* extension = file_type(arguments);    //获得文件后缀名
     char* content_type = "text/plain";     //初始化type='text/plain'
     FILE* read_from;         //本地文件指针 从该文件中读取.html .jpg等
     int readed = -1;         //每次读得的字节数
     
     if (strcmp(extension, "html") == 0) {    //发送内容为html
      content_type = "text/html";
     }
     if (strcmp(extension, "gif") == 0) {    //发送内容为gif
      content_type = "image/gif";
     }
     if (strcmp(extension, "jpg") == 0) {    //发送内容为jpg
      content_type = "image/jpg";
     }
     read_from = fopen(arguments, "r");     //打开用户指定的文件 准备读取
     if(read_from != NULL) {        //指针不为空
      char read_buf[128];        //读文件时的字节缓存数组
      send_header(sock, content_type);    //发送协议头
      send(sock, " ", 2, 0);      //再加一个" " 不能缺少 格式要求
      while(!feof(read_from)) {      //判断文件是否已经结束
       fgets(read_buf, 128, read_from);   //读取
       int len = strlen(read_buf);
       if (sendall(sock, read_buf, &len) == -1) { //发送数据
        printf("Sending error!");    //出现发送错误 显示到控制台 继续发送
        continue;
       }
      }
     }
    }
    /***********************************
     解析并处理用户请求
    ***********************************/
    void handle_req(char* request, int client_sock) {
     char command[BUFSIZ];        //保存解析到的命令字段 GET PUT
     char arguments[BUFSIZ];        //保存解析到的请求的文件
     /*
      * 在用户请求前加上当前目录符号
      */
     strcpy(arguments, "./");       //注意该符号在不同操作系统的区别
     /*
      * 解析请求
      */
     if (sscanf(request, "%s%s", command, arguments+2) != 2) {
      return;           //解析出错在返回
     }
     
     printf("handle_cmd:    %s ",command);    //向控制台输出此时的命令
     printf("handle_path:   %s ",arguments);   //向控制台输出此时的请求路径
     
     if (strcmp(command, "GET") != 0) {     //请求命令格式是否正确
      wrong_req(client_sock);
      return;
     }
     if (not_exit(arguments)) {       //请求的文件是否存在  
      file_not_found(arguments, client_sock);
      return;
     }
     send_file(arguments, client_sock);     //命令格式及请求路径正确则发送数据
     
     return;
    }
    /*************************************
     该方法构造服务器端的SOCKET
     返回构造好的socket描述符
    *************************************/
    int make_server_socket() {
     struct sockaddr_in server_addr;       //服务器地址结构体
     int tempSockId;           //临时存储socket描述符
     tempSockId = socket(PF_INET, SOCK_STREAM, 0);
     
     if (tempSockId == -1) {         //如果返回值为-1 则出错
      return -1;
     }
     /*
      * 填充服务器连接信息
      */
     server_addr.sin_family = AF_INET;
     server_addr.sin_port = htons(SERVER_PORT);
     server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //本地地址
     memset(&(server_addr.sin_zero), '', 8);
     if (bind(tempSockId, (struct sockaddr *)&server_addr,
      sizeof(server_addr)) == -1) {       //绑定服务 如果出错 则返回-1
      printf("bind error! ");
      return -1;
     }
     if (listen(tempSockId, BACKLOG) == -1 ) {     //开始监听
      printf("listen error! ");
      return -1;
     }
     return tempSockId;           //返回取得的SOCKET
    }
    /***********************
     主函数main()
     程序入口
    ***********************/
    void main(int argc, char * argv[]) {
     /*
      * 调用WSAStartup() 便于访问sockets library
      */
     WSADATA wsaData;
     if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
      fprintf(stderr, "WSAStartup failed. ");
      exit(1);
     }
     printf("My web server started... ");
     int server_socket;        //服务器的socket
     int acc_socket;         //接收到的用户连接的socket
     int sock_size = sizeof(struct sockaddr_in);  
     struct sockaddr_in user_socket;     //客户连接信息
     server_socket = make_server_socket();   //创建服务器端的socket
     if (server_socket == -1) {      //创建socket出错
      printf("Server exception! ");
      exit(2);
     }
     /*
      * 主循环
      */
     while(true) {
      acc_socket = accept(server_socket, (struct sockaddr *)&user_socket, &sock_size); //接收连接
      
      //cout << inet_ntoa(user_socket.sin_addr) << endl;    //测试用:-)//
      
      /*
       * 读取客户请求
       */
      int numbytes;
      char buf[100];
      if ((numbytes=recv(acc_socket, buf, 99, 0)) == -1) {
       perror("recv");
       exit(1);
      }
      
      //printf("buf ... %s", buf);      //测试用
      /*
       * 处理用户请求
       */
      handle_req(buf, acc_socket);
     }
    }
    /**************程序结束Server.cpp******************/
  • 相关阅读:
    java8 Date/Time API 新的日期处理工具
    java8 Lambda 表达式和函数式接口快速理解
    java8 新特性精心整理(全)
    Git 从入门到熟练|不敢说精通
    C# Color 列表
    自动控制原理6
    forfiles命令详解
    详解SQLEXPR32_x86_CHS.exe、SQLEXPR_x86_CHS.exe、SQLEXPR_x64_CHS.exe之间的区别
    power disiagner pdb
    vs 中大括号之间垂直虚线显示
  • 原文地址:https://www.cnblogs.com/sanchrist/p/3573295.html
Copyright © 2011-2022 走看看