简易版本的 HTTP 服务器框架的实现

项目:HTTP 服务器框架

步骤:
一. 背景调研(当前不涉及)
二. 需求分析(最核心的一步)
(有产品经理作出完整的文档)

  • 实现一个服务器程序
  • 支持HTTP协议的服务器
  • 从请求角度上,支持GET方法和POST方法
  • 从响应的角度上,支持静态页面和动态页面

     静态页面:返回一个服务器上的本地文件
     动态页面:根据用户的输入实时计算生成一个响应结果页面
    
  • 能够使用浏览器访问

三. 概要设计(分成几个模块,模块之间的关系)
HTTP服务器框架:
这里写图片描述

  1. 通用的HTTP服务器框架(业界知名的服务器框架有:Nginx 、 httpd(apache))
    1). 服务器初始化(socket 的初始化)
    2). 处理HTTP请求

    • 从 socket 中读取数据,并且按照HTTP协议的格式解析数据;
    • 根据输入的请求的不同决定是按照静态页面处理还是动态页面处理;
    • 如果是按照静态页面,直接将服务器上对应的文件返回到客户端中;
    • 如果时按照动态页面,就将输入的信息交给对应的 CGI 程序,由 CGI 程序来计算,生成响应。把响应的结果交还给HTTP服务器框架,由框架完成数据返回到客户端的过程。
  2. 业务相关的 CGI 程序
    1). 根据不同的业务,来分别实现不同的 CGI 程序;
    2). 读取 HTTP 服务器框架交给它的参数,根据自身的业务流程计算生成 HTTP 响应数据,交还给框架;

CGI:是一种标准或协议
CGI 优点:

  • 完成了解耦,让通用的HTTP 服务器框架和业务完全的解耦和
  • CGI 程序可以用任何的编程语言来实现

CGI 的实现过程:
HTTP 服务器框架创建子进程,子进程根据所请求得 CGI 程序的路径(HTTP 请求中的url_path)进行程序替换,CGI 程序就可以根据业务进行计算了。
1. HTTP 服务器是通过什么样的方式把必要的输入参数传给 CGI 程序:进程间通信中的管道 + 环境变量
2. CGI 程序计算的结果通过通过管道写回给客户端

四. 详细设计(模块的实现细节)
五. 具体的编码开发

    1 #include <stdio.h>                                                                                                             
    2 #include <string.h>                                                                                                            
    3 #include <stdlib.h>                                                                                                            
    4 #include <unistd.h>                                                                                                            
    5 #include <pthread.h>                                                                                                           
    6 #include <sys/socket.h>                                                                                                        
    7 #include <netinet/in.h>                                                                                                        
    8 #include <arpa/inet.h>                                                                                                         
    9                                                                                                                                
   10 typedef struct sockaddr sockaddr;                                                                                              
   11 typedef struct sockaddr_in sockaddr_in;                                                                                        
   12                                                                                                                                
   13 #define SIZE (1024*10) //以为宏只是进行简单的文本替换,所以需要加上括号,来避免出现错误                                        
   14 typedef struct HttpRequest                                                                                                     
   15 {                                                                                                                              
   16   char first_line[SIZE]; //C89不行,但C99可以这样写                                                                            
   17   char* method;                                                                                                                
   18   char* url;                                                                                                                   
   19   char* url_path;                                                                                                              
   20   char* query_string;                                                                                                          
   21   int content_length;
   22 }HttpRequest;                                                                                                                  
   23                                                                                                                                
   24 int ReadLine(int sock, char buf[], ssize_t max_size)//ssize_t 无符号长整型                                                     
   25 {                                                                                                                              
   26   // 按行从 socket 中读取数据                                                                                                  
   27   // 实际上浏览器发送的请求中换行符可能不一样。                                                                                
   28   // 换行符可能有:\n, \r, \r\n                                                                                                
   29   // 1.循环从 socket 中读取字符,一次读一个。                                                                                  
   30   char c = '\0';                                                                                                               
   31   ssize_t i = 0; //描述当前读到的字符应该放到缓冲区的哪个下标上                                                                
   32   while(i < max_size -1)                                                                                                       
   33   {                                                                                                                            
   34     ssize_t read_size = recv(sock, &c, 1, 0);                                                                                  
   35     if(read_size <= 0)                                                                                                         
   36     {                                                                                                                          
   37       // 此时认为读取数据失败。即使是recv返回0,由于此时我们                                                                   
   38       // 预期是至少能读到换行标记的。此处很可能是因为收到的                                                                    
   39       // 报文是非法的。                                                                                                        
   40       return -1;                                                                                                               
   41     }                                                                                                                          
   42  
   43     // 对读到的字符进行判定。                                                                                                  
   44     // 2.如果当前字符是 \r                                                                                                     
   45     if(c == '\r')                                                                                                              
   46     {                                                                                                                          
   47       //  a) 尝试从缓冲区读取下一个字符,判定下一个字符是 \n,                                                                  
   48       //      就把这种情况处理成 \n                                                                                            
   49       recv(sock, &c, 1, MSG_PEEK);                                                                                             
   50       if(c == '\n')                                                                                                            
   51       {                                                                                                                        
   52         // 当前的行切割符是一个 \r\n                                                                                           
   53         // 接下来就把下一个 \n 字符从缓冲区中删掉就可以了                                                                      
   54         recv(sock, &c, 1, 0);                                                                                                  
   55       }                                                                                                                        
   56       else                                                                                                                     
   57       {                                                                                                                        
   58         //  b) 如果下一个字符是其他字符,就把 \r 修改成 \n(把                                                                  
   59         //      \r 和 \n 的情况统一在一起)                                                                                     
   60         // 此时行分隔符是 \r,为了方便处理,就把 \r 和 \n                                                                      
   61         // 统一在一起。也就是把 c 中的 \r 改成 \n                                                                              
   62         c = '\n';                                                                                                              
   63       }  
   64     }                                                                                                                          
   
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值