从零开始:编写一个Web服务器---HTTP部分详细讲解以及代码实现(一)

HTTP部分详细讲解以及代码实现

  • HTTP---Hyper Text Transfer Protocol(超文本传输协议)
    • HTTP请求过程
    • HTTP请求分类
    • 状态码(Status-Code)
  • 如何实现HTTP部分?
    • 分析功能
      • Client与Server共有功能
      • Client专属功能
      • Server专属功能
    • 代码实现
      • HTTP类
        • HTTP_CODE含义

本文是从零开始Web服务器的第二篇,本篇将正式从代码入手讲解整个网络编程
给出笔者学习期间接触到的Web服务器代码(C/C++)连接
https://github.com/EZLippi/Tinyhttpd
https://github.com/qinguoyi/TinyWebServer
https://github.com/oatpp/oatpp
本文中的部分代码来自以上项目,感谢他们的开源

HTTP—Hyper Text Transfer Protocol(超文本传输协议)

HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP 是万维网的数据通信的基础。

HTTP请求过程

这里给出一篇参考博客:Http请求过程

HTTP请求分类

请求方法 意义
OPTIONS 请求一些选项信息,允许客户端查看服务器的性能
GET 请求指定的页面信息,并返回实体主体
HEAD 类似于 get 请求,只不过返回的响应中没有具体的内容,用于获取报头
POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改
PUT 从客户端向服务器传送的数据取代指定的文档的内容
DELETE 请求服务器删除指定的页面
TRACE 回显服务器收到的请求,主要用于测试或诊断

下面给出这些请求,在代码层面的表现,如果你想查看自己当前浏览器所发出的HTTP报文,按F12,找到Network,然后F5刷新界面,随便点击Name中的一项,找到Request Headers即可显示出当前的请求头情况。

GET / HTTP/1.1
Host: 192.168.0.23:47310
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*; q = 0.8
Accept - Encoding: gzip, deflate, sdch
Accept - Language : zh - CN, zh; q = 0.8
Cookie: __guid = 179317988.1576506943281708800.1510107225903.8862; monitor_count = 5

状态码(Status-Code)

  • 1xx:表示通知信息,如请求收到了或正在进行处理
    • 100 Continue:继续,客户端应继续其请求
    • 101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到 HTTP 的新版本协议
  • 2xx:表示成功,如接收或知道了
    • 200 OK: 请求成功
  • 3xx:表示重定向,如要完成请求还必须采取进一步的行动
    • 301 Moved Permanently: 永久移动。请求的资源已被永久的移动到新 URL,返回信息会包括新的 URL,浏览器会自动定向到新 URL。今后任何新的请求都应使用新的 URL 代替
  • 4xx:表示客户的差错,如请求中有错误的语法或不能完成
    • 400 Bad Request: 客户端请求的语法错误,服务器无法理解
    • 401 Unauthorized: 请求要求用户的身份认证
    • 403 Forbidden: 服务器理解请求客户端的请求,但是拒绝执行此请求(权限不够)
    • 404 Not Found: 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置 “您所请求的资源无法找到” 的个性页面
    • 408 Request Timeout: 服务器等待客户端发送的请求时间过长,超时
  • 5xx:表示服务器的差错,如服务器失效无法完成请求
    • 500 Internal Server Error: 服务器内部错误,无法完成请求
    • 503 Service Unavailable: 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的 Retry-After 头信息中
    • 504 Gateway Timeout: 充当网关或代理的服务器,未及时从远端服务器获取请求

更多状态码:菜鸟教程:HTTP状态码

更多的HTTP知识讲解请参考另一篇博客——————讲给你听:HTTP

如何实现HTTP部分?

前一部分给到了GET请求的形式,这部分给出POST请求的形式,并给出具体释义

POST / color1.cgi HTTP / 1.1
Host: 192.168.0.23 : 47310
Connection : keep - alive
Content - Length : 10
Cache - Control : max - age = 0
Origin : http ://192.168.0.23:40786
Upgrade - Insecure - Requests : 1
User - Agent : Mozilla / 5.0 (Windows NT 6.1; WOW64) AppleWebKit / 537.36 (KHTML, like Gecko) Chrome / 55.0.2883.87
Safari / 537.36
Content - Type : application / x - www - form - urlencoded
Accept : text / html, application / xhtml + xml, application / xml; q = 0.9, image / webp, */*;q=0.8
Referer: http://192.168.0.23:47310/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Cookie: __guid=179317988.1576506943281708800.1510107225903.8862; monitor_count=281
Form Data
color=gray
第一部分:请求头行,包含请求类型、URI、HTTP协议版本;
请求信息类型通常有:get、post、put等等;  
第二部分:即紧跟第一行之后的,请求头部,包含服务器所使用的说明信息;
接下来解释一下这些说明信息的意思:
1、host:请求web服务器的域名地址
2、Connection: 表示是否持久连接;即keep-alive表示持久连接;
3、Cache-Control:指定请求和响应的缓存机制;no-cache(不能缓存)、no-store(在请求消息中发送将使得请求和响应消息都不使用缓存)、  max-age(客户机可以接收生存期不大于指定时间(以秒为单位)的响应)、max-stale(客户机可以接收超出超时期间的响应消息)、min-fresh(客户机可以接收响应时间小于当前时间加上指定时间的响应)、  only-if-cached等等
4、User-Agent: HTTP协议运行的浏览器类型的详细信息;比如:谷歌/67.0.3396.995、Accept: 指浏览器可以接收的内容类型;
6、Accept-Encoding: 客户端浏览器可以支持的web服务器返回内容压缩编码类型;
7、Accept-Language:浏览器支持的语言类型,
8、Cookie: 某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密);例如当我们上网时,某些网站能准确的推送我们想要的信息。  
第三部分:"\r\n" --> 分割header和body部分的分界线

分析功能

Client与Server共有功能

通过刚才的简单讲解,明显看出,在这一步骤中我们要完成的只有一个任务,就是解析字符串
因为我们拿到的是“字符串”,好了这样的任务就转化为了,如何用C++进行字符串解析。相信大家在学习C++的过程中,做过不少这方面的题。那么做好一个针对于HTTP报文的字符串的解析,就要先看一下,字符串是什么样的形式,我们需要做到哪几步。

  • 是否需要分类? 答:需要,不然刚才那么多请求不白讲了
  • 是否需要分段解析? 答:需要,Http报文按照行区分出信息
  • 是否需要判断特殊字符? 答:需要,(显而易见是吧)
  • 是否需要区分大小写 答:需要

Client专属功能

Server专属功能

读者可能会问,我看的别人写的HTTP教程都是要什么线程池,然后socket什么。。。。
其实我想写这个教程的目的就是按照我自己的接受思路,由点串成线,由线串成面。 如果一次性的写出完整实现,就失去了分步走的目的。

当然我的最终目的是通过这些博客,可以一步步的把所学的知识落到代码然后串起来,而不是一下子全摆出来。

代码实现

在进行编程时一定要注意好,定义与实现的分文件形式。良好的代码习惯就是在.h中构建出全部的功能框架,然后在cpp中完成实现。

HTTP类

class http_conn
{
     
public:
	//报文的请求方法
    enum METHOD
    {
     
        GET = 0,
        POST,
        HEAD,
        PUT,
        DELETE,
        TRACE,
        OPTIONS,
        CONNECT,
        PATH
    };
    //报文解析的结果
    enum HTTP_CODE
    {
     
        NO_REQUEST = 0,
        GET_REQUEST,
        BAD_REQUEST,
        NO_RESOURCE,
        FORBIDDEN_REQUEST,
        FILE_REQUEST,
        INTERNAL_ERROR,
        CLOSED_CONNECTION
    };
public:
	//构造和析构
	//如果需要,后期在这里回填
    http_conn() {
     }
    ~http_conn() {
     }
public:
	//报文解析函数
	//返回的是解析出来的情况,根据不同的值完成不同的操作
	void process();
	//完成回应的函数
	void response();
//可以看出我们给出的处理函数和回应函数都是没有参数的
//意味着我们需要在对象内部定义出:读缓冲区,写缓冲区,
private:
	//存储读取的请求报文数据
    char m_read_buf[READ_BUFFER_SIZE];
    int m_read_idx;
    //m_read_buf读取的位置m_checked_idx
    int m_checked_idx;
    //m_read_buf中已经解析的字符个数
    int m_start_line;
	
	//存储发出的响应报文数据
    char m_write_buf[WRITE_BUFFER_SIZE];
    //指示buffer中的长度
    int m_write_idx;
}

HTTP_CODE含义

表示HTTP请求的处理结果,在头文件中初始化了八种情形

  • NO_REQUEST
    • 请求不完整,需要继续读取请求报文数据
  • GET_REQUEST
    • 获得了完整的HTTP请求
  • NO_RESOURCE
    • 请求资源不存在
  • BAD_REQUEST
    • HTTP请求报文有语法错误或请求资源为目录
  • FORBIDDEN_REQUEST
    • 请求资源禁止访问,没有读取权限
  • FILE_REQUEST
    • 请求资源可以正常访问
  • INTERNAL_ERROR
    • 服务器内部错误

以上就是我们初步对于HTTP部分的一个原始构思。
这里你不需要纠结为什么为void返回值,因为这只是一个大概的框架,我们需要不断地细化这部分的变成最终确定process,response返回什么。
当然,这里的处理我们写的相当粗糙。因为我们虽然考虑到的有这么多情况,但是只是交给了一个函数处理,这会导致代码的复用性下降,同时整个处理代码会很庞大。
接下来第二步,就是为process response减负。
从零开始:编写一个Web服务器—HTTP部分详细讲解以及代码实现(二)

你可能感兴趣的:(Web服务器,C++学习记录,c++,服务器,web服务器)