代码开源:
webserver_HTTP
使用了线程池,通过epoll实现的Proactor版本的web服务器。参考了游双老师的《Linux高性能服务器编程》以及牛客网的《Linux高并发服务器开发》课程。在自己复现的基础上进行模块的整合并添加一些小更改。所有代码拥有完备的注释。访问的资源在 同级目录"resources"文件夹中
目录
6.1 HTTP协议
6.1.1 HTTP协议简介
超文本传输协议(Hypertext Transfer Protocol , HTTP )是一个简单的请求 - 响应协议,它通常运行在 TCP 之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的 头以 ASCII 形式给出;而消息内容则具有一个类似 MIME 的格式。 HTTP 是万维网的数据通信的基础。
6.1.2 HTTP协议概述
HTTP 是一个客户端终端(用户)和服务器端(网站)请求和应答的标准( TCP )。通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP 请求到服务器上指定端口(默认端口为 80 )。应答的服务器上存储着一些资源,比如 HTML 文件和图 像。我们称这个应答服务器为源服务器( origin server )。在用户代理和源服务器中间可能存在多个 “中 间层 ” ,比如代理服务器、网关或者隧道( tunnel )。通常,由HTTP 客户端发起一个请求,创建一个到服务器指定端口(默认是 80 端口)的 TCP 连接。HTTP 服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比 如 "HTTP/1.1 200 OK" ,以及返回的内容,如请求的文件、错误消息、或者其它信息。
6.1.3 HTTP协议工作原理
HTTP 协议定义 Web 客户端如何从 Web 服务器请求 Web 页面,以及服务器如何把 Web 页面传送给客户端。HTTP 协议采用了请求 /响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、 URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。
因此,客户端发起请求,向源服务器中请求数据,这个请求被称为“HTTP请求报文”,主要结构由下文展示。源服务器接到请求报文后,进行相应的业务处理(文件存在可访问,则准备好文件并发送,没有则发回错误信息),这个回应被称为“HTTP相应报文”,具体的结构也将在后文揭示。
6.2 HTTP请求报文格式
如图,HTTP请求报文有4个部分
- 请求行(只有一行)标识了请求方法、URL、协议条约
- 请求头(含有若干行),主要结构是key-value结构,即头部字段:值,本次项目中,主要解析keep-alive,Connection,Host,Content-Length这4个字段
- 空行,标志着请求头结束,接下来是请求数据
- 请求数据,其字节数由请求头中的Content-Length决定
GET / HTTP/1.1Host: www.baidu.comUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp, / ;q=0.8Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflate, brConnection: keep-aliveCookie: BAIDUID=6729CB682DADC2CF738F533E35162D98:FG=1;BIDUPSID=6729CB682DADC2CFE015A8099199557E; PSTM=1614320692; BD_UPN=13314752;BDORZ=FFFB88E999055A3F8A630C64834BD6D0;__yjs_duid=1_d05d52b14af4a339210722080a668ec21614320694782; BD_HOME=1;H_PS_PSSID=33514_33257_33273_31660_33570_26350;BA_HECTOR=8h2001alag0lag85nk1g3hcm60qUpgrade-Insecure-Requests: 1Cache-Control: max-age=0
如上是一个请求报文的样例
需要注意的是,每行的结束部分是"\r\n"来判断的
本项目的分析逻辑是:将每个"\r\n"都替换成"\0\0"调用指向每个部分开始的字符串进行分析,这样"\0"可以直接作为分析部分的哨兵,从而简化操作。
6.3 HTTP响应报文格式
HTTP响应报文如图,其也有4个部分
- 状态行(只有一行)标识了请求方法、URL、协议条约
- 响应头(含有若干行),主要结构是key-value结构,即头部字段:值,本次项目中,响应报文主要有:正文长度(Content-Length)、正文类型(Content-Type)、连接类型(Connection)、发送时间(Date)
- 空行,标志着请求头结束,接下来是请求数据
- 响应数据,其字节数由请求头中的Content-Length表示
HTTP/1.1 200 OK
Bdpagetype: 1Bdqid: 0xf3c9743300024ee4Cache-Control: privateConnection: keep-aliveContent-Encoding: gzipContent-Type: text/html;charset=utf-8Date: Fri, 26 Feb 2021 08:44:35 GMTExpires: Fri, 26 Feb 2021 08:44:35 GMTServer: BWS/1.1Set-Cookie: BDSVRTM=13; path=/Set-Cookie: BD_HOME=1; path=/Set-Cookie: H_PS_PSSID=33514_33257_33273_31660_33570_26350; path=/; domain=.baidu.comStrict-Transport-Security: max-age=172800Traceid: 1614329075128412289017566699583927635684X-Ua-Compatible: IE=Edge,chrome=1Transfer-Encoding: chunked
如上是响应报文的样例。
产生响应报文相比起来比较简单,只要调用分析后的各个数据然后拼合在一起即可,本项目为了提高代码复用,封装了一个带有vsnprintf函数的函数来对写缓冲区进行写入,提高了代码复用。
前文提到了writev写的是两个部分,第一部分是状态行+响应头+空行,第二部分是响应正文。
6.4 HTTP请求/响应的步骤
- 客户 端连接到 Web 服务器
- 一个H TTP客户端,通常是浏览器,与 Web 服务器的 HTTP 端口(默认为 80 )建立一个 TCP 套 接 字连接。例如,http://www.baidu.com。(URL)
- 发送 HTTP 请求
- 通过 TCP 套接字,客户端向 Web 服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据 4 部分组成。
- 服务器接受请求并返回 HTTP 响应
- Web 服务器解析请求,定位请求资源。服务器将资源复本写到 TCP 套接字,由客户端读取。一个 响应由状态行、响应头部、空行和响应数据 4 部分组成。
- 释放连接 TCP 连接
- 若 connection 模式为 close,则服务器主动关闭 TCP连接,客户端被动关闭连接,释放 TCP 连接;若connection 模式为 keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;
- 客户端浏览器解析 HTML 内容
- 客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的 HTML 文档和文档的字符集。客户端浏览器读取响应数据 HTML,根据 HTML 的语法对其进行格式化,并在浏览器窗口中显示。
6.5 HTTP请求方法
1. GET :向指定的资源发出 “ 显示 ” 请求。使用 GET 方法应该只用在读取数据,而不应当被用于产生 “副作用 ” 的操作中,例如在 Web Application 中。2. HEAD :与 GET 方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文 部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中 “关于该 资源的信息 ” (元信息或称元数据)。3. POST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含 在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。4. PUT :向指定资源位置上传其最新内容。5. DELETE :请求服务器删除 Request-URI 所标识的资源。6. TRACE :回显服务器收到的请求,主要用于测试或诊断。7. OPTIONS :这个方法可使服务器传回该资源所支持的所有 HTTP 请求方法。用 '*'来代替资源名称,向 Web 服务器发送 OPTIONS 请求,可以测试服务器功能是否正常运作。8. CONNECT : HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。通常用于 SSL加密服务器的链接(经由非加密的 HTTP 代理服务器)。
本项目只有解析GET的部分,其余部分还待开发