http权威指南 第一部分(基础)

时间:2021-03-25 05:01:29

http使用的是可靠的数据传输,因此即使数据从地球一端传递到另外一端也能保证数据的正确性。对于开发人员来说这是一件好事,因为就不必担心数据完整性的问题了。http属于应用层的协议,在使用http的时候无需担心网络细节,因为联网的细节都交给了它更低一层的TCP/IP因特网传输协议。

URI、URL、URN

URI称作统一资源标识符,URL为统一资源定位符,URN为统一资源名。其中URI包含了URL和URN。但是目前URN还处于试验阶段,未大范围使用,所以可以理解为URI就是URL了。
URL是根据资源的绝对位置来进行资源的查找,比如说http://www.baidu.com/file/index.html就是在主机地址为www.baidu.com下的文件夹file查找index.html。但是如果该文件被转移到了其他地址,那么再次访问就会出现404错误访问不到。这就是URN来解决的问题,URN叫做统一资源名,作为资源的唯一名称存在,与资源所处的位置无关。通过这样就可以把资源四处搬移。

http报文格式:

http报文分为请求报文和响应报文。http报文包括以下三个部分的内容:起始行、首部字段、主体。
请求报文的语法格式如下:

  <method> <request-URL> <version>
<headers>

<entity-body>

响应报文的格式如下:

<version> <status> <reason-phrase>
<headers>

<entity-body>

下面是各部分的简要介绍:
< method>方法:是指客户端希望服务器对资源执行的操作,是一个单独的词,有如下方法:

方法名 解释
GET 从服务端获取一份文档,无主体(< entity-entry>)部分.
POST 向服务的发送需要处理的数据,通常用它来支持HTML的表单处理,有主体
PUT 将请求的主体部分存储到服务端,有主体
HEAD 只从服务端获取文档的首部,可以在不获得资源的情况下了解资源的情况,可以通过查看响应中的状态码看某个对象是否存在,可以通过查看首部来测试资源是否被修改,无主体
TRACE 对可能经过代理服务器传送到服务器上去的报文进行追踪,报文行程的最后一站的服务器会弹回一个响应消息,并在响应消息的主体中携带请求报文,而这个报文中便携带者报文经过的路径,无主体
OPTIONS 决定在服务器上执行哪些方法,无主体
DELETE 从服务器上删除一份文档,无主体

< version> :版本号,格式为HTTP/x.y的形式,因为每个http版本所遵循的协议不同,通过这个就可以将应用程序所支持的协议告知对方。
< status>:状态码,用返回服务端处理请求的状况。

整体范围 已定义范围 分类
100-199 100-101 信息提示
200-299 200-206 成功
300-399 300-305 重定向
400-499 400-415 客户端错误
500-509 500-505 服务端错误

< reason-phrase>:用于描述响应状态,人类可读,无固定标准。
< request-url>:请求的资源路径或者是完整的URL。
< headers>:首部信息,分为通用首部(请求报文和响应报文都可用)、请求首部(请求报文单用)、响应报文(响应报文单用)、实体首部(用来描述http实体部分的内容)、扩展首部(非标准首部)。对于首部的详细信息,会在后面逐渐介绍到。

URL语法

大多数url的语法都建立在如下的通用格式上:

<scheme>://<user>:<password>@<host>:<port>/<path>;<param>?<query>#<frag>
  1. 方案< scheme>:使用什么协议,如http,ftp等。
  2. 主机与端口:主机组件标识了要访问的宿主机器,端口号标识下层的tcp协议所监听的端口,http默认为80。
  3. 用户名和密码:许多服务器需要输入用户名和密码才能进行访问,如ftp服务器,如果没有输入用户名和密码,则会插入一个默认的值。
  4. 路径:指明资源所在的路径。
  5. 参数:为应用程序提供所需要的额外信息。比如:http://host/ham;sale=false/index.html;grphpics=true。目前还没见过这种应用场景。
  6. 查询字符串:就是在?后面传递的键值对。
  7. 片段:不会发送到服务端,用于应用程序如浏览器进行文档内部的定位。

url中的字符:由于需要兼容全世界各地的字符请求,所以对一些特殊字符,如~空格等都会进行转义,转义的方式就是%后面跟着两个标识字符ASCII的十六进制数。

连接管理

http下层利用的是tcp/ip可靠的数据连接。http连接的性能在很大程度上取决于tcp的连接,最常见的tcp相关时延包括:

  • tcp连接建立握手
  • tcp慢启动拥塞控制(tcp连接会随着时间进行自我调谐,起初会限制最大速度,如果传输成功便会逐渐提高自己的传输速度)
  • 数据聚集的nagle算法(该算法试图在tcp发送前将tcp数据绑定在一起以提高网络效率。首先小的http请求可能无法填满分组,会因为等待永远无法到来的数据而产生时延,其次该算法会阻止数据的发送,直到有确认的分组抵达为止。)
  • 用于捎带确认的tcp延迟确认算法
  • time_wait时延和端口耗尽(当tcp断开连接时,会在内存中维护一个小控制块,用来记录最近所关闭连接的IP地址和端口号,这个维护会持续一小段时间,此时关闭的IP地址和端口号是无法使用的,所以端口号的数量限制了每秒请求的次数。)

Connection首部字段的理解

Connection首部字段有一个逗号分隔的连接标签列表,这些标签为此连接指定了不会传递到其他连接中的选项,比如可以用Connection:close来说明下一条报文以后必须关闭连接。

对于HTTP/1.0所支持的持久连接,可以通过包含Connection:Keep-Alive首部请求将一条连接保持打开的状态,也可以通过Keep-Alive:max=5,timeout=120指定持久连接所处理的最大报文数量和延续的最长时间。但是却存在一种哑代理的状况。

客户端发送保持长连接的http请求后,如果下一跳为一个代理,而该代理并不能理解Connection字段的含义,所以该代理就会把报文原封不动地向后发送,直到服务器接收。服务器接收后发现有Connection:keep-alive首部,然后他就会认为客户端需要保持长连接,所以返回一个包含Connection:keep-alive的长连接的响应报文,同样,那个不支持的代理会原封不动地转发数据,最终客户端和服务器都认为已经建立了长连接,但是代理却完全不知道是怎么回事,当客户端发送下一个请求时,因为代理在转发完报文后在等待关闭,所以他会忽略客户端发送来的报文,导致客户端一直处于挂起状态,直到连接超时关闭。

对此的一个解决方案是如下:
添加一个Proxy-connection字段代替Connection字段的长连接请求,当代理遇到这个字段时,如果支持Connection字段,则会把Proxy-connection改为Connection字段进行传输,如果服务端遇到Connection字段,那么证明代理已经把Proxy-connection进行了处理,如果服务端遇到Proxy-connection,则直接拒接长连接请求。
但是这种方案对于中间有一个代理的情况可以解决,但是对于多个代理的情况就无法解决了。

对于HTTP/1.1,长连接是个默认值,除非响应中包含了Conneciont:close,不然http就会维持长连接的状态。

管道化连接

这是相对于持续连接的一个优化,当第一条请求的响应还没返回之前,持续发送第二条第三条数据,这样做降低了网络回环的时间,提高性能。对于管道化连接,有几条限制:

  • 如果客户端无法确认连接是持久的,则不应该使用管道
  • 必须按照与请求相同的顺序回送http相应。
  • http客户端必须做好连接随时中断的准备
  • http不应该用管道化的方法发送回产生副作用的请求,比如post。

对于第四点做一个解释,对于get请求,如果管道关闭了,可以进行重复请求。但是对于post,提交请求后,管道关闭,但是并不知道服务器到底是否已经处理了这个请求,如果重复提交,可能存在处理多个表单的情况,比如京东买东西的时候多下了一次单。所以,管道请求需要的是幂等事务,即不管重复多少次和重复一次的效果是一样的。