Kopite Kopite的博客

http协议

2017-05-22
Kopite

客户端与服务器交互时需要用到http协议,本文进行整理。

简介

http协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的一种网络传输协议。http协议是基于TCP/IP协议的应用层协议,它不涉及数据包(packet)传输,主要规定了客户端和服务器之间的通信格式,默认使用80端口。

http/0.9

请求格式

http/0.9只有一个命令GET,如下示例中,TCP连接(connection)建立后,客户端向服务器请求(request)网页index.html

GET /index.html

回应格式

http/0.9版本规定服务器只能回应html格式的字符串,服务器发送完毕就关闭TCP连接。

<html>
  <body>Hello World</body>
</html>

http/1.0

在1996年5月发布的HTTP/1.0版本中:

  • 可以发送任何格式的内容,这使互联网从此不仅可以传输文字,还可以传输图像、视频以及二进制文件
  • 除了已有的GET命令,HTTP/1.0还引入了POSTHEAD命令,丰富了浏览器与服务器的交互方式
  • http请求和回应的格式也有所改变,除了数据部分,每次通信都必须包括头信息(http header),它被用来描述一些元数据
  • 其他新增的功能还包括状态码(status code)、多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等

请求格式

GET / HTTP/1.0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
Accept: */*

上述请求示例代码中,第一行是请求行,必须在尾部添加协议版本HTTP/1.0,后面是请求头信息,用来描述客户端的情况。

  • Accept —— 声明客户端可以接受的数据类型,Accept: */*表明客户端可以接受任意类型的数据
  • Accept-Encoding —— 声明客户端可以接受的数据压缩方法,如gzip、deflate等

回应格式

服务器回应如下所示,回应的格式:响应行 + 响应头部 + 空行 + 响应数据,其中响应行协议版本 + 状态码(status code) + 状态描述组成。

HTTP/1.0 200 OK 
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

<html>
  <body>Hello World</body>
</html>
  • Content-Type —— 服务器回应的数据类型,这些数据类型称为MIME(Multipurpose Internet Mail Extensions)。可以在MIME尾部使用分号用来附加参数,如下所示,服务器回应的是网页并且编码方式为utf-8
    Content-Type: text/html; charset=utf-8
    
  • Content-Encoding —— 由于服务器回应的数据类型可以任意,Content-Encoding指定服务器所回应数据的压缩方法,如gzip、compress、deflate等

缺点

http/1.0版本的缺点在于每个TCP连接只能发送一个请求,数据发送完毕,连接就关闭,如果还要请求其他资源,就需要再新建一个连接,而TCP连接的新建成本很高,因为客户端和服务器需要三次握手,并且开始时发送速率较慢(slow start),http/1.0版本性能较差,随着网页加载的外部资源越来越多,这个问题愈发突出。

为解决此问题,有些浏览器在请求时,使用了一个非标准的Connection属性,如下所示。

Connection: keep-alive

这个属性要求服务器不关闭TCP连接,以便供其他请求复用,服务器同样回应这个属性,如下所示。

Connection: keep-alive

一个可以复用的TCP连接就建立了,直到客户端或服务器主动关闭连接,但是Connection并不是标准属性。

http/1.1

持久连接

http/1.1版本于1997年1月发布,它进一步完善了http协议。该版本引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不需要声明Connection: keep-alive

当客户端和服务器发现对方有一段时间没有活动,就可以主动关闭连接,相对规范的做法:客户端在最后一个请求时发送Connection: close,明确要求服务器关闭TCP连接。

Connection: close

管道机制

http/1.1版本还引入了管道机制(pipelining),即在同一个TCP连接里面,客户端可以同时发送多个请求,提升http协议的效率。例如,客户端需要请求两个资源时,以前的做法是在同一个TCP连接里面,先发送A请求,然后等待服务器做出回应,客户端收到服务器回应后再发出B请求。http/1.1版本引入的管道机制允许浏览器同时发出A请求和B请求,但是服务器还是按照顺序,先回应A请求,完成后再回应B请求。

一个TCP连接现在可以传送多个回应,势必就要有一种机制来区分数据包是属于哪一个回应,Content-length属性的作用:声明本次回应的数据长度。

Content-Length: 3495

上面示例中告诉浏览器,服务器本次回应的长度是3495个字节,后面的字节属于下一个回应。在http/1.0版本中,Content-Length属性不是必需,因为浏览器发现服务器关闭TCP连接时,就表明浏览器已经收到全部数据包。

使用Content-Length属性的前提条件是:服务器发送回应之前,必须知道回应的数据长度。对一些很耗时的动态操作来说,这意味着服务器要等到所有操作完成才能发送数据,导致效率并不高。

分块传输编码

http/1.1版本引入了分块传输编码(chunked transfer encoding),产生一块数据就发送一块,采用流模式(stream)取代缓存模式(buffer)。

因此,http/1.1版本规定可以不使用Content-Length属性,转而使用分块传输编码,只要请求或回应的头信息有Transfer-Encoding属性,就表明回应将由数量未定的数据块组成。

Transfer-Encoding: chunked

每个非空的数据块之前,会有一个16进制的数值表示这个块的长度,最后一个大小为0的块表示本次回应的数据发送完毕,示例如下

HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

25
This is the data in the first chunk

1C
and this is the second one

3
con

8
sequence

0

其他功能

  • http/1.1新增多个命令:PUTPATCHHEADOPTIONSDELETE
  • 客户端请求头信息中新增Host属性,该属性指定服务器的域名,如下所示。
Host: www.example.com

缺点

虽然http/1.1允许复用TCP连接,但是在同一个TCP连接里面,所有的数据通信是按次序进行的,服务器只有处理完一个回应才会进行下一个回应,一旦前面的回应特别慢,后面就会有许多请求排队等待,这称为队头堵塞(Head-of-line blocking)

为避免此问题,只有两种方法:一是减少请求数,二是同时多开持久连接,由此产生很多网页优化技巧,比如合并脚本和样式表、将图片嵌入css代码、域名分片(domain sharding)等。

http/2

http/2于2015年发布,它由google开发的SPDY协议演化而来。http协议简介部分系转载,详见:http协议入门

原理

http协议工作于C/S、B/S架构,客户端通过URL向服务器发送请求,web服务器根据接收到的请求,向客户端发送回应信息,默认使用80端口。

http协议定义了客户端如何向服务器请求数据,以及服务器如何把响应数据传送给客户端。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据,服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。

http请求/响应的步骤:

  • 客户端连接到服务器。客户端与服务器的端口(默认为80)建立一个TCP套接字连接,例如http://www.oakcms.cn
  • 客户端发送http请求。通过TCP套接字,客户端向服务器发送请求报文,一个请求报文由请求行请求头部空行请求数据4部分组成
  • 服务器接受请求并返回http响应。服务器解析请求,定位请求资源,并将资源复本写到TCP套接字,由客户端读取,一个响应由响应行响应头部空行响应数据4部分组成
  • 释放TCP连接。如果Connection属性为close,服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;如果Connection属性为keep-alive,则该连接会保持一段时间,在该时间内可以继续接收请求
  • 客户端解析html内容。客户端首先解析状态行,查看请求状态码,然后解析响应头

在浏览器地址栏输入URL,按下回车键后的请求响应流程:

  1. 浏览器向DNS服务器请求解析该URL中域名所对应的ip地址
  2. 解析出ip地址后,根据该ip地址和端口同服务器建立TCP连接
  3. 浏览器发出读取文件(URL中域名后面部分对应的文件)的http请求,该请求报文作为TCP三次握手中第三个报文的数据发送给服务器
  4. 服务器对浏览器请求作出响应,并把对应的html文本发送给浏览器
  5. 释放TCP连接
  6. 浏览器接收html文本并显示内容

http协议的主要特点如下:

  • 媒体独立。任意类型的数据都可以通过http协议发送,客户端及服务器指定合适的MIME内容类型即可
  • 无状态。无状态是指http协议对事务处理没有记忆能力,如果后续处理需要前面的信息时,需要进行重传,这将导致每次连接传送的数据量变大

客户端请求消息格式由请求行(request line)请求头部(request header)空行请求数据(request body)四部分组成,如下图所示。

  • METHOD表示请求类型,例如POSTGET
  • path-to-resoure表示请求的资源
  • HTTP/Version-number表示http协议的版本号
  • GET请求时request body部分为空

服务器响应也由四部分组成:响应行(response line)响应头部(response header)空行响应数据(response body),如下图所示。

URI与URL

URI(Uniform Resource Identifier,统一资源标识符)用来唯一的标识一个资源,web上可用的每种资源如html文档、图像、视频片段、程序等都使用URI来定位。

URI一般由三部组成:访问资源的命名机制存放资源的主机名资源自身的名称(由路径表示)

URL(Uniform Resource Locator,统一资源定位符)是一种具体的URI。笼统的说,每个URL都是URI,但每个URI不一定都是URL,这是因为URI还包括URN。URL用来标识某一处资源的地址,以下述URL为例来介绍URL的各部分组成。

http://www.aspxfans.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name
  • 协议部分,该URL的协议部分为http:,表示使用了http协议,在互联网中可以使用多种协议,如http、ftp等,后面的//为分隔符
  • 域名部分,该URL的域名部分为www.aspxfans.com,也可以使用ip地址作为域名
  • 端口部分,跟在域名后面的是端口,域名和端口之间使用:作为分隔符,端口不是必须,省略端口时将使用默认端口
  • 虚拟目录部分,从域名后的第一个/开始到最后一个/止是虚拟目录部分,虚拟目录也不是URL中必须的部分。本例中的虚拟目录是/news/
  • 文件名部分,从域名后的最后一个/开始到止是文件名部分;如果没有?,则从域名后的最后一个/开始到#止是文件名部分;如果没有#,那么从域名后的最后一个/开始到URL结束都是文件名部分,文件名不是URL中必须的部分,省略时将使用默认的文件名。本例中的文件名是index.asp
  • 参数部分,从开始到#止是参数部分,又称搜索部分、查询部分,允许有多个参数,多个参数之间用&分隔。本例中的参数部分是boardID=5&ID=24618&page=1
  • 锚部分,从#开始到最后是锚部分,锚不是URL中必须的部分。本例中的锚部分是name

状态码

访问网页时,浏览器会向服务器发出请求,在浏览器接收并显示网页前,服务器会返回一个包含http状态码(HTTP Status Code)的信息头(server header)用以响应浏览器的请求。

http状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用,http状态码共分为5种类型,如下图所示。

常见的状态码如下:

  • 200 - 请求成功
  • 301 - 资源(网页等)被永久转移到其它URL
  • 404 - 请求的资源(网页等)不存在
  • 500 - 内部服务器错误

GET与POST请求的区别

GET示例

chrome浏览器,博客园首页搜索框中输入java,使用Fiddler工具查看Request、Response信息,如下所示:

URL

https://www.cnblogs.com/


request

GET http://zzk.cnblogs.com/s?t=b&w=java HTTP/1.1
Host: zzk.cnblogs.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 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,en;q=0.6
Cookie: __utma=226521935.1751255329.1471419249.1475723920.1475723920.1; __utmt=1; __utma=59123430.1751255329.1471419249.1498449233.1498449233.1; __utmb=59123430.1.10.1498449233; __utmc=59123430; __utmz=59123430.1498449233.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _ga=GA1.2.1751255329.1471419249; _gid=GA1.2.1232116599.1498397326; _gat=1


response

HTTP/1.1 200 OK
Date: Mon, 26 Jun 2017 03:55:01 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Vary: Accept-Encoding
Cache-Control: private
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Content-Length: 28192

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title> java - 博客园找找看</title>
    <link rel="shortcut icon" href="/Content/Images/favicon.ico" type="image/x-icon" />
    <link rel="stylesheet" href="//at.alicdn.com/t/font_1476446892_1265016.css"/>
    <link href="/bundles/vendor/css/search?v=TPZ2g11WKxuQMoDW28Ba86EiiXG-IC754vBdoWpS_ck1" rel="stylesheet"/>

    <link href="/bundles/css/common?v=YnyBIbVcjYfvSniBrK1MruBuU3tVNDUW8efOvdBtlzA1" rel="stylesheet"/>

    <link href="/bundles/css/search?v=7Idlz8AusWLK0z-1fKUFATCMjb4f-bYnikvDTCoFLm41" rel="stylesheet"/>

    <script src="//common.cnblogs.com/script/jquery.js" type="text/javascript"></script>
    <script src="/bundles/vendor/js/search?v=NpLlfw4LpMCUZhjzyVCso1691Ad_n968KGHO0lB-39o1"></script>

    <script src="/bundles/js/common?v=ySabubpZbwHmGc1mYw-D1HcILTcuF3A5bYjm7gz9Ilg1"></script>

    <script src="/bundles/js/search?v=gJDH3XYMp1k0TzoPyxL-VTFkjSL1aEkZGvvVPe4qeyg1"></script>

</head>
<body>
         ...
</body>
</html>

POST示例

chrome浏览器,hospital项目登录页中输入账号及密码,使用Fiddler工具查看Request、Response信息,如下所示:

URL

localhost:8080/hospital/login.jsp


request

POST http://localhost:8080/hospital/api/user/login HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 38
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:8080
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:8080/hospital/login.jsp
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: SESSION=71c064a9-a420-4173-81ad-ed928445574b; Hm_lvt_9519c406f5a0c72b10a515c2cea174f9=1498303263,1498351236,1498375768,1498441810; Hm_lpvt_9519c406f5a0c72b10a515c2cea174f9=1498449901

email=soyuone%40sina.cn&password=admin


response

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 26 Jun 2017 04:10:51 GMT

21
{"code":200,"msg":"登录成功"}
0

区别

  • GET请求的参数附在URL之后;POST请求的参数放在request body中传输,URL并不会改变
  • http协议没有对传输数据的大小进行限制,但在实际开发中存在限制。GET请求传输的数据量较小,一般不能大于2KB;POST请求传送的数据量较大,通常认为POST请求参数的大小不受限制,但实际上web服务器会对POST请求时的数据大小进行限制,POST请求传输的数据量总比GET请求传输的数据量大
  • POST请求的安全性比GET请求的安全性高

  • 参考:http协议详解
  • 参考:http协议入门
  • 参考:https升级指南
  • 参考:http协议小结
  • 参考:http协议-菜鸟教程

Comments