在了解了HTTP的前世今生后,就有必要去深入了解下HTTP协议了,本节从HTTP报文整体结构出发,奠定对HTTP协议的整体认识,方便今后的深入学习。
一、HTTP协议特点
HTTP的特点总结如下:
- 支持客户端/服务器模式
- 客户端/服务器模式工作的方式是由客户端主动向服务器发出请求,服务器端响应请求,并行相应服务。
- 简单快速
- 客户向服务器请求服务时,只需传送请求方法和路径。
- 请求方法常用的有GRT、HEAD、POST。每种方法规定了客户端与服务器联系的类型不同。
- 由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
- 灵活
- HTTP允许传输任意类型的数据对象。
- 正在传输的类型由Content-Type加以标记
- 无连接
- 无连接的含义时限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户端应答后,即断开连接。早期的时候,由于大部分时间是空闲的,为了尽快释放资源。不过现在HTTP1.1改为默认启用keepalive长连接机制,因此现在的HTTP已经不再是无连接的了。
- 无状态
- HTTP协议是无状态协议。
- 无状态是指每个请求都是互相独立、毫无关联的,协议不要求客户端或服务器记录请求相关的信息。
- 另一方面,在服务器不需要先前信息时,它的应答就较快。这样,HTTP协议就比较简单,不记仇。
二、蜜汁兄弟-URL和URI
啊,为什么搞这么多概念来闹晕我们。
- URI:Uniform Resource Identifier,统一资源标识符
- URL:Uniform Resource Location,统一资源定位符
- URN:Uniform Resource Name,统一资源名称
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wkMTrVE8-1613545405556)(http://bloghello.oursnail.cn/network25-1.png)]
从上图我们可以看到,URI=URN+URL。URL时URI的子集。如何理解呢?
URI 就是来独一无二的表示一种资源(这种资源可以是人、某台电脑,手机等)。那么问题来了,你怎么去实现这种独一无二的表示?或者说,你怎么去独一无二的表示地球上的一个人?你会说我用身份证号码。好了,那这个身份证号码,就是URI的一种实现方式。那怎么来独一无二的来标记网上的资源,你通过这个标记,来定位网上某个独一无二的资源呢?那就是通过URL,所以说URL是URI的一种具体实现。
三、HTTP报文结构
还记得我们的传输层、网络层吗,他们都有自己的头部信息来标识。HTTP 协议也是与 TCP/UDP 类似,同样也需要在实际传输的数据前附加一些头数据,不过与 TCP/UDP 不同的是,它是一个“纯文本”的协议,所以头数据都是 ASCII 码的文本,可以很容易地用肉眼阅读,不用借助程序解析也能够看懂。
用于HTTP的协议交互的信息被称为HTTP报文。请求端的HTTP报文叫做请求报文,响应端的叫做响应报文。
3.1 请求报文
HTTP的这两种报文都由四部分组成:请求行(起始行)、请求头(首部字段)、空行、请求体(报文主体)。
- 请求行:包含【请求的方法如GET、POST】,【请求URI】和【HTTP版本】,中间用空格分隔
- 请求头:包含请求的各种条件和属性,使用key:value的形式展示
- 空行:它的作用是通过一个空行,告诉服务器请求头部到此为止。
- 请求体:实际传输的数据,不一定是文本,可以是图片视频等二进制文件。若方法字段是GET,则此项为空,没有数据;若方法字段是POST,则通常来说此处放置的就是要提交的数据。
其中,请求行和请求头可以合称为请求头,即常说的 header ,而请求体是我们常说的 body 。
请求头整体结构大概就是:
HTTP协议规定报文必须有 header ,可以没有 body ,比如常用的 GET 请求。且 header 和 body 中间必须有个空行。
一个GET请求一般实际上长这个样子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5voO5V9J-1613545405570)(http://bloghello.oursnail.cn/network25-3.png)]
在这个浏览器发出的请求报文里,第一行“GET / HTTP/1.1”就是请求行,其中 GET 就是请求方法, / 就是请求的 URI ,HTTP/1.1 就是HTTP协议及版本;而后面的“Host”、“Connection”等等都属于 header,报文的最后是一个空白行结束,没有 body。
对于POST请求而言,几乎一样,只是请求方法变成POST,空行下面会有请求参数构成的Body。
3.2 响应报文
同样的,HTTP响应报文也由四部分组成:响应行(状态行)、响应头、空行、响应体(报文主体)。
- 响应行:响应行一般由【协议版本】、【状态码如200】及其【描述】组成 比如 HTTP/1.1 200 OK
- 响应头:响应头用于描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会儿它回送的数据。
- 空行:它的作用是通过一个空行,告诉客户端请求头部到此为止。
- 响应体:响应体就是响应的消息体,如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码,如此之类。
响应行和响应头整体结构大概为:
来个实际例子,如请求一个HTML页面的请求报文和响应报文:
请求头和响应头的结构是基本一样的,唯一的区别是起始行。
HTTP 头字段非常灵活,不仅可以使用标准里的 Host、Connection 等已有头,也可以任意添加自定义头,这就给 HTTP 协议带来了无限的扩展可能。
四、HTTP请求方法
- 实际上GET和POST用的最多,其他的用的都比较少。GET和POST有一点区别:
- GET请求的参数是放在URL后面拼接的,而POST是放在了body中
- 浏览器对GET请求参数的长度是有限制的,而POST则没有此限制
- 基于第一个,安全性上,POST比GET要强点,比如用户名密码拼接在URL后面的话,那么下一个用户可能根据浏览器浏览器记录找到你的登录链接,从而登录上你的账号密码
- POST和PUT很类似了,不过也有区别:
- 他两本意上的区别:新建一条记录的话就用post,而更新一条记录的话就用put,PUT可以认为是具备幂等性的,而POST是非幂等的。
- PUT基本不用,无验证机制存在安全问题,POST可以借助代码逻辑实现幂等性
- HEAD是简略版的GET,只用获取响应头即可,不需要获取具体内容。
关于幂等性,它的定义是:
Methods PUT and DELETE are defined to be idempotent, meaning that multiple identical requests should have the same effect as a single request.
这意味着,PUT这样的幂等方法在多次请求时,产生和单次请求同样的效果。即没有产生新的资源。反之,如果多次请求时产生了副作用,则应该使用非幂等方法,对应POST。
DELETE为什么是幂等的?因为删除一次和删除多次是一样的影响。GET为什么是幂等的就不赘述了。
五、HTTP状态码
状态代码的第一个数字代表当前响应的类型:
- 1xx 消息:Informational(信息性状态码),表示请求已被服务器接收,继续处理。
- 2xx 消息:Success(成功状态码),表示请求已成功被服务器接收、理解、并接受。
- 200 OK:表示成功
- 204 No Content:服务器成功处理了请求,但不需要返回任何实体内容。如果客户端是浏览器的话,那么用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化。
- 206 Partial Content:服务器已经成功处理了部分 GET 请求。类似于 FlashGet 或者迅雷这类的 HTTP 下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。该请求必须包含 Range 头信息来指示客户端希望得到的内容范围。
- 3xx 消息:Redirection(重定向状态码),表示需要后续操作才能完成这一请求。
- 301 Moved Permanently:被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。
- 302 Found:请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。
- 4xx 消息:Client Error(客户端错误状态码),表示请求含有语法错误或者无法被执行。
- 400 Bad Request:语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。请求参数有误。
- 401 Unauthorized:当前请求需要用户验证。
- 403 Forbidden:服务器已经理解请求,但是拒绝执行它。
- 404 Not Found:请求失败,请求所希望得到的资源未被在服务器上发现。
- 5xx 消息:Server Error(服务器错误状态码),表示服务器在处理某个正确请求时发生错误。
- 500 Internal Server Error:服务器遇到了不知道如何处理的情况。这个时候一般是服务出现BUG了。
- 502 Bad Gateway:此错误响应表明服务器作为网关需要得到一个处理这个请求的响应,但是得到一个错误的响应。
- 503 Service Unavailable:表明服务器暂时处于超负载或正在进行停机维护,无法处理请求。
- 504 Gateway Timeout:当服务器作为网关,不能及时得到响应时返回此错误代码。
六、HTTP的头部字段
头部的字段帮助客户端和服务端更好地交互,这里先拿几个重要字段说明下。
6.1 Accept和Content-Type
数据交互的时候,我们需要解决三个问题:数据类型协商(数据类型、压缩算法)、语言类型协商、字符集编码类型协商。解决了这三个问题,两者之间就可以愉快地进行交流了。
【①数据类型协商和压缩算法协商】
首先我们要知道数据是有佷多类型的,比如分为文本类型有:text/html、text/plain、text/css等;有图片image/gif、image/png等,有音频和视频:audio/mepg、audio/mp4等。也有我们常用的不固定的数据格式,由上层应用来解释的比如application/json。
这些类型有个名字,叫做:MIME Type,但是HTTP传输的时候会进行数据压缩,因此还有一个字段叫做:Encoding Type,告诉对方数据用了什么压缩格式,好让对方还原出来原始数据。Encoding Type类型就少了佷多,一般用的是:gzip、deflate、br等压缩算法。
好了,有了 MIME type 和 Encoding type,无论是浏览器还是服务器就都可以轻松识别出 body 的类型,也就能够正确处理数据了。通过什么字段呢?
客户端可以通过 Accept 字段告诉服务器希望接收什么格式的数据,服务器可以用 content-type 告诉客户端实际发送了什么格式的数据。
上图中,意思是客户端告诉服务器,我只能看懂HTML、XML的文本和数据,还有就是webp和png格式的图片,其他的我不认识。服务器返回的时候告诉客户端本次返回内容的格式和压缩算法。
【②语言类型协商】
除了格式的协商,还有语言和编码的协商,如果客户端只认识英文,而服务器返回了中文,不就尴尬了。
客户端会发送Accept-Language表明自己看得懂的语言,比如zh-CN 的汉语文字。
Accept-Language: zh-CN, zh, en
而服务器会在响应头中用Content-Language告诉客户端实体数据用的语言类型。
Content-Language: zh-CN
【③字符集编码类型协商】
而字符集编码是客户端发送Accept-Charset字段来说明,比如自己只能处理GBK和UTF8字符编码,其他的不行。服务器如果返回UTF8编码如何返回呢?逐一响应头没有对应的Content-Charset,而是在ontent-Type字段的数据类型后面用“charset=xxx”来表示。
Accept-Charset: gbk, utf-8
Content-Type: text/html; charset=utf-8
这边不能乱,总结下:
- 数据类型表示实体数据的类型,是文本还是图片等,相关的头字段是 Accept 和 Content-Type;
- 压缩算法表示实体数据的压缩方式,相关的头字段是 Accept-Encoding 和 Content-Encoding;
- 语言类型表示实体数据的自然语言,相关的头字段是 Accept-Language 和 Content-Language;
- 字符集表示实体数据的编码方式,相关的头字段是 Accept-Charset和 Content-Type;
不过现在的浏览器都支持多种字符集,通常不会发送 Accept-Charset,而服务器也不会发送 Content-Language,因为使用的语言完全可以由字符集推断出来,所以在请求头里一般只会有 Accept-Language 字段,响应头里只会有 Content-Type字段:
6.2 Host、Connection、Referer、User-Agent
- Host:指定被请求资源的IP和PORT
- Referer:告诉服务器我是从哪个页面链接过来的
- User-Agent:客户端所使用的操作系统和浏览器名称、版本
- Connection:控制是否开启长连接,如果是keep-alive说明是开启长连接,同一个客户端的请求可以复用一个TCP连接。反之close就不复用TCP连接。
HTTP的头佷多,下面用几张图来预览下,后续将根据实际情况再对某些头部字段进行详解。