HTTP
HTTP(HyperText Transfer Protocol,HTTP)是实现Web的应用层协议,HTPP由两个程序实现:一个客户端程序和服务器程序。这两个程序运行在两个设备上,使用HTTP报文进行会话。在深入HTTP之前,先解释一些Web的术语。
Web相关概念
Web页面(Web page):Web页面是由对象组成的。一个对象(object)是一个文件,这个文件可以是HTML、JPRG图形、一个JAVA程序或一段视频这样的文件。
多数Web页面会包含一个HTML基本文件以及引用的几个对象
Web浏览器(Web browser):Web浏览器是实现了HTTP协议的客户端(client)。
Web服务器(Web server):Web服务器是实现了HTTP协议的服务器,能够存储Web对象,每个对象由URL寻址获得。
HTTP概况
HTTP定义了Web浏览器向Web服务器请求Web页面的方式、服务器向Web浏览器发送页面的方式。基本的思路如下图:
HTTP使用TCP作为运输层协议,在服务器和客户端建立起TCP连接后,浏览器和客户端进程就能通过套接字(socket)访问TCP。
Web服务器能够从套接字获得从Web客户端发送的HTTP请求报文,同样Web客户端能从套接字获得从Web服务器发送的HTTP响应报文。当HTTP报文通过套接字进入TCP,报文就会脱离程序的控制并进入TCP的控制。这就是划分网络协议层的原因,我们无需关注TCP的工作,只需要关注应用层(HTTP)怎样处理报文。
无状态协议
HTTP是一个无状态协议,因为HTTP服务器不会保存用户的任何信息。但这不代表Web服务器不会保存任何信息,HTTP提供了cookie以支持Web服务器跟踪用户内容。
用go创建一个Web服务器和客户端
在浏览器或者使用curl命令访问本地 http://127.0.0.1:8080 ,会返回一个”welcome to this Web Page”
1 | package main |
或者编写一个客户端发送HTTP请求
1 | package main |
代码来源:http编程 · Go语言中文文档 (topgoer.com)
非持续连接和持续连接
在客户端和服务器进行通信前,必须先建立起连接,然后客户端就能够发送一系列请求。客户端-服务器的连接是通过TCP建立起的,应用程序的设计者就需要考虑一个问题,即每个请求/响应对是经过一个TCP连接发送,还是所有请求/响应对都经过一个TCP连接发送。
这两种方式分别称为:
- 非持续连接:每次发送一个HTTP请求都需要建立一个TCP连接,在客户端收到HTTP响应后关闭TCP连接。
- 持续连接:发送一系列HTTP请求可以在一个TCP连接得到响应,服务器会在一段时间后关闭TCP连接。
下图中展示了三种HTTP连接的方式,也是HTTP模型发展的三个阶段:HTTP/1.0、HTTP/1.1、HTTP/2.0。前两者是我们介绍的非持续连接和持续连接的方式实现的,第三个采用了流水线的持续连接设计思路允许在同一TCP连接中并发的处理HTTP请求和回复HTTP响应。
可以看出这三种方式的优劣:
HTTP/1.0会频繁建立TCP连接,这会给服务器带来严重的负担。HTTP/1.1改善了这个问题,它使TCP连接保持打开,等待一个时间间隔(没有被使用)后关闭连接。HTTP/2.0能够比前两者减少更多的延迟,但不是所用请求都适合使用这种方式,这里不作过多介绍。
HTTP报文格式
HTTP报文有两种:请求报文和响应报文、
HTTP请求报文
HTTP请求报文主要由三部分组成:
- 请求行(request line):包含三个字段
- 方法字段:HTTP常见的方法包括GET、POST、HEAD、PUT和DELETE
- URL:客户端请求的对象
- HTTP版本:一般客户端默认版本使HTTP/1.1
- 首部行(header line):首部行是向服务器或客户端提供一些必要的信息,比如:Host指明对象所在的主机,Connect指明发送完该对象后是否关闭(Keep-Alive/close),User-agent指明用户代理(向用户发送请求的浏览器类型)。
- 实体体(entity body):实体体可以看作报文运输的货物。一般使用Post方法的报文会携带数据,这个数据可能来自网页的输入。
下面展示一个HTTP请求报文,第一行是请求行,其中使用方法是GET,请求对象 /,之后三行是首部行。GET方法一般没有实体体。
1 | GET / HTTP/1.1 |
可以在浏览器使用F12打开开发者工具查看网络中的HTTP请求,Edge、Google等主流浏览器都支持。
HTTP响应报文
HTTP响应报文与HTTP请求相似,但第一行与它不同,我们分析HTTP响应报文的状态行:
状态行分为三部分:
- 版本:服务器使用HTTP版本,一般是HTTP/1.1
- 状态码和短语:状态码是表明特定HTTP是否完成成功,短语与状态码相对应。常见响应如下
- 200 OK:请求成功,信息返回响应报文中
- 301 Move Permanently:请求的对象被永久转移,新的URL定义在响应报文首部行的 Localtion。客户软件会自动获取新的URL
- 400 Bad Requset:一个通用差错代码,指示该请求不能被服务器理解
下面展示一个HTTP响应报文,响应报文也能在浏览器中查看。
1 | HTTP/1.1 200 OK |
Cookie-解决HTTP无状态的设计
前面我们提到HTTP本身是一个无状态的协议,这能够简化服务器的设计,并且允许工程师去开发同时能处理上千个。
假如一个网站希望能够识别用户,可能是服务器用于限制用户的访问,或者是因为它想将内容与用户联系起来。这个时候,我们就能够使用cookie对用户进行跟踪,目前大多数Web站点都是用了cookie技术。
cookie实现需要实现四个组件
- HTTP请求报文的首部行有cookie
- HTTP响应报文的首部行有cookie
- 用户端系统中有一个cookie文件,一般由用户的浏览器进行管理
- Web站点有一个后端数据库
cookie工作过程
- PC端访问浏览器的Web对象,请求报文到达Web服务器后,服务器会创建一个识别码,以此作为索引在它的后端数据库中产生的表项。
- Web服务器会在HTTP响应报文的首部行添加
Set-cookie:识别码
,PC端接收后会根据Set-cookie在其特定的cookie文件中添加一行。 - PC之后向该服务器继续访问Web对象时会在HTTP请求报文首部行添加cookie字段,服务器可以根据请求报文中cookie跟踪用户动作或完成一些动作。
- PC端在一周后继续访问,加入服务器的cookie没有过期,服务器能够继续使用该cookie跟踪用户。
go代码模拟
我们使用go实现上述流程,我们假设一个场景:
- 用户Alice是这个网站注册用户,首先Alice通过index网站登录,此时服务器发送HTTP响应报文时会携带一个Set-cookie发送给她的PC浏览器。
- 然后Alice要在这个网站下获得一些需要的信息,Alice向该网站的另一个资源发出请求,发送的HTTP请求报文携带有cookie。
- 网站服务器通过cookie确定Alice的身份,然后根据Alice在数据库中的信息推荐给她需要的信息。
服务器代码
1 | package main |
客户端
1 | package main |
Web缓存
web缓存是能够通过暂时存储Web对象,以减少服务延迟的技术。如果客户端频繁访问某几个页面,那么使用Web缓存存储这些Web对象会减少大量时延。
web缓存分类:
- 数据库缓存
- CDN缓存
- 浏览器缓存
- 代理服务器缓存
代理服务器
web缓存器(Web cache)也叫代理服务器(proxy server),它是能够代表初始Web服务器来满足HTTP请求的网络实体。
Web缓存器的运行如下:
- PC端浏览器会向代理服务器创建TCP连接,并向代理服务器发送一个HTTP请求。
- 如果代理服务器有PC浏览器请求的对象,代理服务器就能直接返回该对象。
- 如果没有请求的对象,代理服务器就会和初始服务器建立TCP连接,并发送一个HTTP请求。然后初始服务器会发送HTTP响应给Web缓存器。
- 当代理服务器中没有该对象时,它会在本地存储一个该对象副本,并向PC浏览器使用HTTP响应报文发送该副本(通过现有的PC浏览器和代理服务器的TCP连接)。
反向代理
代理服务器除了作为Web缓存器外它还具有其他功能,比如拦截和过滤HTTP请求和响应、隐藏网络内部IP等。上面使用的代理服务器是作为正向代理。
正向代理是指代理服务器在客户端和服务器之间。客户端发送请求到代理服务器,代理服务器将请求转发给目标服务器,并将目标服务器的响应转发回客户端(代理服务器常与客户端在同一局域网下)。
正向代理常用于加强安全、缓存内容以加速访问、访问受限资源等场景。
代理服务器还能用于反向代理,反向代理是指代理服务器在服务器和客户端之间。客户端发送请求到反向代理服务器,反向代理服务器将请求转发给真实服务器,并将真实服务器的响应转发回客户端(反向代理服务器常与服务器在同一局域网下)。
反向代理常用于负载均衡、高可用性、加强安全等场景。
条件GET
缓存能够很好减少用户感受到的响应时间,但是也引入了一个新问题:如果缓存中引入的副本已经过时了,或者说服务器上文件已经更新了但缓存的副本还没更新。
对此,HTTP使用了条件GET解决这种问题,它的解决方式如下:
- 允许代理服务器向服务器发送使用GET方法的请求报文
- 这个报文的请求头包含一个
if-Modified-Since
当代理服务器从客户端接受到一个请求对象,这个对象正好存储在代理服务器中。此时代理服务器发现缓存一段时间没有更新,它会向服务器发送一个条件GET请求。如果服务器没有修改过该对象,响应报文是不会包含对象副本。
参考
- 计算机网络(自顶向下方法)第七版
- 终于有人把正向代理和反向代理解释的明明白白了!-腾讯云开发者社区-腾讯云 (tencent.com)
- WEB缓存 - 掘金 (juejin.cn)