HTTP 协议的定义
这篇文章暂时不研究 HTTP 底层的 TCP/IP 的握手和挥手过程, 只从表面的交互流程分析 HTTP 协议.
HTTP 英文全称是 Hypertext Transfer Protpcol, 也就是超文本传输协议. HTTP 是一个标准, 定义了 web 客户端如何与服务器对话以及数据如何从服务器传回到客户端. 在日常开发和使用过程中, HTTP 经常被认为是一种用于传输 html 文件和文件中内嵌的图片的协议或者手段, 实际上 HTTP 是一种通用的网络数据传输格式, 它的传输内容不仅仅局限于 HTML 文件或者图片, 也可以用来传输 Microsoft Word 文档甚至是 Windows 的 exe 文件等等, 所有可以用字节序列表示的数据都可以使用 HTTP 进行传输.
HTTP 通过 TCP/IP 进行数据传输, 如果忽略底层的 TCP 协议的握手和挥手的细节, 对于从客户端到服务器的每一个请求和请求的响应, 在 HTTP1.0 有下面几个步骤:
1, 默认情况下, 客户端在端口 80 开启与服务器的一个 TCP 连接, 当然也可以指定其他的端口.
2, 客户端向服务器发送消息, 请求指定路径上的资源. 一个 HTTP 请求包括一个首部, 可选项包括一个空行和这次请求的数据.
3, 服务器向客户端发送响应. 响应以响应码开头, 接着是包含元数据的首部, 可选项包括一个空行以及所请求的文档数据或者错误信息.
4, 服务器关闭 TPC 连接.
在 HTTP1.1(目前最常用的就是 HTTP1.1)以及以后的 HTTP 版本中, 可以通过一个 TCP 连接连续发送多个请求和接收多个响应. 也就是说, 上面的 1 和 4 步骤中间的 2 和 3 步骤可以反复执行多次. 另外, HTTP1.1 中, 请求数据和响应数据可以分块发送, 提高了扩展性.
HTTP 请求方法
HTTP 中定义了多种请求方法, 用于标识当次请求需要完成什么类型的操作, 常用的 HTTP 请求方法有 GET,HEAD,PUT,POST,PATCH,TRACE,OPTIONS,DELETE.
HTTP 请求方法 | 描述 | 是否安全 | 是否幂等 |
---|---|---|---|
GET | 通常用于请求服务器获取某个资源 | 是 | 是 |
HEAD | 类似于 GET,但是响应结果中不包含响应体,只包含协议信息和首部,通常用于测试资源是否存在或者是否被修改 | 是 | - |
POST | 客户端向服务器提交数据 (支持 HTML 的表单数据),可能会导致新的资源的建立或者已有资源的修改 | 否 | 否 |
PUT | 从客户端向服务器传送的数据取代指定的文档的内容 (全部取代) | 否 | 是 |
PATCH | 客户端向服务器传送的数据取代指定的文档的内容 (部分取代) | 否 | 是 |
TRACE | 回显客户端请求服务器的原始请求报文,用于 "回环" 诊断 | 是 | - |
OPTIONS | 请求服务器获取服务器支持的各种功能,可以询问服务器支持什么类型的 HTTP 方法,一般用于性能测试 | 是 | - |
DELETE | 请求服务器删除指定的资源 | 否 | 否 |
上面说到的 "是否安全" 的选项是 "是", 意味着使用该种 HTTP 请求方法不会发生任何数据的修改或者更新动作, 也就是请求多次也不会影响到资源的状态. 如果 "是否幂等" 的选项是 "是", 意味着使用该 HTTP 请求方法请求多次 HTTP 调用, 无论调用多少次, 请求结果或者资源的状态是一样的(可以理解为只有首次调用是真正修改了资源的状态, 从第二次调用开始后面的调用只获取到第一次调用的结果).HTTP 方法的安全性和幂等性是我们在设计 HTTP 接口时候需要重点考虑的两个因素.
值得注意的是: 上面提到的 POST 和 PUT 方法的功能可以理解为相同的, 两者的主要区别在于 POST 不是幂等的, 而 PUT 是幂等的. 在目前的 Web 开发中, POST 方法已经被滥用, 一般很少人会使用 PUT, 除非是推崇 RESTFUL 风格编程. PUT 方法和 PATCH 方法的功能类似, 都是用客户端请求的数据去替换掉服务器中指定文档中的内容, 不过 PUT 方法是全部替换, 而 PATCH 方法是部分替换.
PS: 上面的方法只是 HTTP 协议中的请求方法的一些规范, 没有硬性规定一定要遵循.
常见的 HTTP 状态码
JDK 中常见的 HTTP 状态码可以在类 java.NET.HttpURLConnection 中找到, 总结一下如下:
状态码 | 状态码消息 | 含义 | HttpURLConnection 中的常量 | 简单描述 |
---|---|---|---|---|
1xx | - | 信息状态码。 | - | 不常见,暂不考虑 |
100 | Continue | 服务器准备接受请求主体,客户端发送请求主体;这允许客户端在请求发送大量数据之前询问服务器是否接受请求。 | - | 不常见,暂不考虑 |
101 | Switching Protocols | 服务器接受客户端在 Upgrade 首部字段中要求改变应用的协议请求,如从 HTTP 转换为 WebSockets。 | - | 不常见,暂不考虑 |
2xx | - | 表示请求成功。 | - | - |
200 | OK | 最常见的响应码,代表请求成功。如果请求方法是 GET 或者 POST,所请求的数据与正常的首部都包含在响应体中。如果请求方法是 HEAD,则只包含首部信息。 | HTTP_OK | 处理请求成功 |
201 | Created | 服务器已经在响应体中指定的 URL 创建了对应的资源。客户端现在应当尝试加载该 URL。这个响应码只在响应 POST 请求时发送。 | HTTP_CREATED | 创建成功 |
202 | Accepted | 表示请求已经被处理,但是处理尚未结束,所以不会返回任何响应数据。 | HTTP_ACCEPTED | 接受请求 |
203 | Non-Authoritative Information | 由缓存代理或者其他本地源返回资源的表示,不能保证是最新的。 | HTTP_NOT_AUTHORITATIVE | 无权威的返回结果 |
204 | No Content | 服务器已经成功处理了该请求,但是没有信息发回给客户端。一般是由于服务器上的表单处理逻辑的问题,只接收数据不返回数据。 | HTTP_NO_CONTENT | 无返回内容 |
205 | Reset Content | 服务器已经成功处理了该请求,但是没有信息发回给客户端。客户端应该清除发送请求的表单信息。 | HTTP_RESET | 重置内容 |
206 | Partial Content | 服务器返回客户端请求的资源的部分内容,而不是整个文档。 | HTTP_PARTIAL | 部分内容 |
3xx | - | 重定向。 | - | - |
300 | Multiple Choices | 服务器为所请求的文档提供一组不同的表示。 | HTTP_MULT_CHOICE | 多重选择 |
301 | Moved Permanently | 资源已经移动到一个新的 URL。客户端应当自动加载这个 URL 的资源。 | HTTP_MOVE_PERM | 永久移动 |
302 | Moved Temporarity | 资源暂时移动到一个新的 URL,但其位置在不久的将来还会再次改变。 | HTTP_MOVE_TEMP | 临时移动 |
4xx | - | 客户端错误 | - | - |
400 | Bad Request | 客户端向服务器发出的请求使用了不正确的语法。 | HTTP_BAD_REQUEST | 错误请求 |
401 | Unauthorized | 访问这个 URL 需要身份验证,一般是用户名和口令。 | HTTP_UNAUTHORIZED | 未授权 |
403 | Forbidden | 服务器理解请求,但是有意拒绝进行处理。 | HTTP_FORBIDDEN | 禁止访问 |
404 | Not Found | 最常见的错误响应,指示服务器找不到所请求的资源。 | HTTP_NOT_FOUND | 未找到资源 |
405 | Method Not Allowed | 请求方法不支持用于请求指定的资源。 | HTTP_BAD_METHOD | 方法禁用 |
406 | Not Acceptable | 所请求的资源不能以客户端希望的格式提供,客户端期望的格式由请求 HTTP 首部 Accept 字段指定。 | HTTP_NOT_ACCEPTABLE | 不接受 |
5xx | - | 服务端错误 | - | - |
500 | Internale Server Error | 服务器内部异常。 | HTTP_SERVER_ERROR | 服务器异常 |
501 | Not Implemented | 服务器不具备完成请求的功能。 | HTTP_NOT_IMPLEMENTED | 尚未实现 |
502 | Bad Gateway | 服务器作为网关或代理,从上游服务器收到无效响应。 | HTTP_BAD_GATEWAY | 错误网关 |
503 | Service Unavailable | 服务器暂时无法处理请求,可能是超负荷或者维护等原因。 | HTTP_UNAVAILABLE | 服务不可用 |
简单概括如下:
响应码 100-199 表示一个提供信息的响应.
响应码 200-299 表示请求成功.
响应码 300-399 表示重定向.
响应码 400-499 表示一个客户端引发的错误.
响应码 500-599 表示一个服务器引发的错误.
常见的 HTTP 首部
下面简单列举一些比较常用的首部以及它们的作用.
User-Agent
User-Agent 一般作为请求首部, 用于告知服务器当前客户端使用的是什么浏览器, 翻译过来就是用户代理, 作用是允许服务器响应请求时候针对客户端用户代理的类型优化返回的数据或者文件. 例如使用 Chrome 发送请求时, User-Agent 如下:
- User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36
- Host
Host 一般作为请求首部, 用于指定接收该请求的服务器的主机名和端口号. 例如:
- Host: www.importnew.com
- Accept
Accept 一般作为请求首部, 它的作用是告知服务器它可以使用或者想要什么已经不能使用或者不想要什么. 下面是几个 Accept 首部以及它们的作用:
首部 | 作用 |
---|---|
Accept | 告知服务器客户端可以接收和处理哪些媒体类型 |
Accept-Charset | 告知服务器客户端可以接收和处理哪些字符集 |
Accept-Encoding | 告知服务器客户端可以接收和处理哪些编码方式 |
Accept-Language | 告知服务器客户端可以接收和处理哪些语言 |
Accept 首部用于指定接收媒体类类型的时候, 需要指定类型和子类型, 这是因为媒体类型 (MIME) 本来就是按二级分类的, 例如 JPEG 图像的媒体类型是 image/jpeg, 类型是 image, 子类型是 jpeg.MIME 已经定义了八种顶级的类型:
- text/* 表示人可读的文字.
- image/* 表示图片.
- model/* 表示 3D 模型, 如 VRML 文件.
- audio/* 表示音频.
- video/* 表示多媒体图片, 视频, 也可能是音频.
- application/* 表示二进制数据.
- message/* 表示协议特定的信封, 如 Email 消息和 HTTP 响应.
- muitipart/* 表示多个文档和资源的容器.
- 举个例子, 如果客户端只接收 JSON 数据:
- Accept: application/JSON
- Referer
- Referer 一般作为请求首部, 它提供了包含当前请求的 URL 的文档的 URL, 也就是当前请求的上一个来源的文档, 一般用作防盗链. 例如 www.baidu.com/search?name=doge, 服务器在处理此请求的时候, 需要判断 Referer 是否为 www.baidu.com,www.baidu.com/search 的上一个文档来源必须是 www.baidu.com, 否则服务器应该拒绝该请求.
- Cookie
- Cookie 一般作为请求首部, 客户端通过它向服务器传送一个或者多个令牌, 原则上 Cookie 并不是安全的首部, Cookie 的内容也会缓存在客户端. 一般在 Servlet 应用中, Cookie 是识别当前用户, 实现持久会话的最佳方式. 从过期时间分类来看, Cookie 分为会话 Cookie 和持久 Cookie, 会话 Cookie 的过期时间比较短, 持久 Cookie 的过期时间比较长或者不会过期, Cookie 的过期策略等控制应该由服务端控制. 由于 Cookie 是直接暴露在客户端, 一般不能使用 Cookie 存放敏感的数据, 需要存放敏感数据可以考虑使用数据加密处理.
- Cookie: uid=10086; domain="localhost"
- Set-Cookie
- Set-Cookie 一般作为响应首部, 和 Cookie 对应, 表示服务器设置成功的 Cookie.
- Cache-Control
- Cache-Control 一般作为请求首部, 告知服务器对当前的请求的响应结果进行缓存相关操作. Cache-Control 支持的值比较多, 这里不展开细节, 常见的如 no-cache 表示在没有成功通过源站校验的情况下不得使用缓存, 如 max-age 表示响应结果需要缓存到指定的最大时间.
- Content-Type
- Content-Type 是通用首部, 可以作为请求首部或者响应首部, 它的作用是告知服务器或者客户端当前请求或者响应结果的内容 (媒体) 类型.
- Content-Length
- Content-Length 是通用首部, 可以作为请求首部或者响应首部, 它的作用是告知服务器或者客户端当前请求或者响应数据体的长度.
- Content-Encoding
- Content-Encoding 一般作为响应首部, 与 Accept-Encoding 对应, 用于服务器告知客户端当前响应结果的内容编码.
- Content-Language
- Content-Language 一般作为响应首部, 与 Accept-Language 对应, 用于服务器告知客户端当前响应结果的内容语言.
- Connection
- Connection 一般作为请求首部, 表示是否需要持久连接. 在 HTTP1.1 中, 如果指定为 Keep-Alive, 可以提供持久连接, 提高 Socket 的复用率从而降低多次连接的性能消耗. 下面有一个小节专门介绍 Keep-Alive.
- Orgin
- Origin 一般作为请求首部, 指明当前的请求是一个针对跨域资源共享的请求(该请求要求服务器在响应中加入一个 Access-Control-Allow-Origin 的消息头, 表示访问控制所允许的来源).
- Origin: http://www.baidu.com
- Access-Control-Allow-Origin
- Access-Control-Allow-Origin 一般作为响应首部, 和 Origin 对应, 表示服务器允许的该跨域资源共享的请求来源.
- Access-Control-Allow-Origin: http://www.baidu.com
- Server
- Server 一般作为响应首部, 用于告知客户端服务器的相关信息.
- HTTP 请求体
- 如果采用 GET 请求方法, 只需要向远处服务器提供 URL,URL 中的路径和查询字符串就可以匹配到需要查询的资源. 但是 URL 中无法提供详细的客户端信息. 另外, 像 POST 和 PUT 这些请求方法所携带的数据体有可能比较大, 无法放在 URL 的查询字符串. 因此 HTTP 需要请求体. HTTP 请求体包括下面四个部分:
- 1, 一个起始请求行, 包括 HTTP 方法, 路径, 查询字符串以及 HTTP 版本.
- 2,HTTP 请求的首部.
- 3, 一个空行(两个连续的回车或者换行对).
- 4, 请求数据体.
- 文字描述可能比较抽象, 用图表示如下:
- PS:space 代表空格,\r\n 代表换行.
- 举个例子:
- GET /wp-admin/admin-Ajax.PHP?postviews_id=23996&action=postviews&_=1538708851063 HTTP/1.1
- Host: www.importnew.com
- Connection: keep-alive
- Pragma: no-cache
- Cache-Control: no-cache
- Accept: */*
- X-Requested-With: XMLHttpRequest
- User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36
- Referer: http://www.importnew.com/23996.HTML
- Accept-Encoding: gzip, deflate
- Accept-Language: zh-CN,zh;q=0.9
- postviews_id=23996&action=postviews&_=1538708851063
HTTP 响应体
响应体和请求体的格式类似, 主要是返回服务器的响应数据到客户端, 包括服务器的一些信息和响应数据体. HTTP 响应体主要包括下面的四个部分:
1, 一个起始响应行, 包括 HTTP 版本, 状态码, 状态码描述.
2,HTTP 响应的首部.
3, 一个空行(两个连续的回车或者换行对).
4, 响应数据体.
文字描述可能比较抽象, 用图表示如下:
PS:space 代表空格,\r\n 代表换行.
举个例子:
- HTTP/1.1 200 OK
- Server: nginx
- Date: Fri, 05 Oct 2018 03:07:37 GMT
- Content-Type: text/HTML; charset=UTF-8
- Transfer-Encoding: chunked
- Connection: keep-alive
- Keep-Alive: timeout=2
- Vary: Accept-Encoding
- X-Powered-By: PHP/5.3.3
- X-Robots-Tag: noindex
- X-Content-Type-Options: nosniff
- X-Frame-Options: SAMEORIGIN
- Content-Encoding: gzip
- 2995
- Keep-Alive
在使用 HTTP1.0 的时候会为每个请求打开一个新的 TCP 连接, 实际上, 这导致了一个典型 Web 会话中打开和关闭所有连接所花费的事件远远大于实际传输数据所消耗的时间, 特别是响应结果包含很多小文档的会话. 对于使用 SSL 或者 TLS 加密的 HTTPS 连接, 这个问题更加严重, 因为建立一个安全的 Socket 的握手过程远比建立常规的 Socket 需要更多的工作.
在 HTTP1.1 和后面的版本中, 服务器不必在返送响应之后就关闭连接. 已经建立的连接可以保持打开, 在同一个 Socket 上等待来自客户端的新请求. 简单来说, 就是可以在一个 TCP 连接上连续发送多个请求和连续进行多个请求的响应.
客户端可以在 HTTP 请求首部中添加一个 Connection 请求头, 指定值为 Keep-Alive, 这样就能实现 Socket 的重用:
Connection: Keep-Alive
HTTP1.1 或者之后的版本, Keep-Alive 是默认开启的, 不需要显式指定, 如果需要关闭可以设置为 close:
Connection: close
一旦开启了 Keep-Alive, 服务器在关闭一个 Socket 连接之前, 如果有新的客户端再次连接到服务器, 那么就是重用 Socket. 在 JDK 中可以通过系统属性来控制如果使用 HTTP 的 Keep-Alive:
http.keepAlive: 默认值为 true, 默认开启 HTTP 的 Keep-Alive.
http.maxConnections: 同时保持打开的 Socket 数量的最大值, 默认值为 5.
http.keepAlive.remainingData: 默认值为 false, 如果设置为 true, 则 JDK 在丢弃连接之后会完成剩余数据的清理.
sun.NET.http.errorstream.enableBuffering: 默认值为 false, 如果设置为 true, 则尝试缓存 400 和 500 状态码的相对小的错误流, 从而能释放连接以备后续使用.
sun.NET.http.errorstream.bufferSize: 为缓存错误流的缓冲区的字节大小, 默认值为 4096 字节, 只有上一项为 true 的时候才有意义.
sun.NET.http.errorstream.timeout: 默认值为 300ms, 读取错误流超时的毫秒数.
Cookie 和 Cookie 管理
很多网站使用一些小文本串在连接之间存储持久的客户端状态, 这些小文本串称为 Cookie(中文翻译为: 小甜点).Cookie 在请求和响应的首部从服务器传到客户端, 再从客户端传回服务器, 服务器使用 Cookie 来指示 sessionID, 购物车内容, 登录凭据等.
除了简单的 name=value 对, Cookie 可以有多个属性来控制它们的作用域, 包括过期日期, 路径, 域, 端口, 版本和安全选项.
JDK 中 java.NET.CookieStore 类提供了对 Cookie 的增删查操作, 它的默认实现是 java.NET.InMemoryCookieStore, 如果实现 CookieStore,JDK 中的 Cookie 默认是存放在内存中的. 另外, java.NET.CookieManager 内部持有 CookiePolicy 和 CookieStore, 定义了一系列管理 Cookie 的方法, 一般通过 CookieManager 操作 Cookie, 当然也可以通过实现 CookieStore, 覆盖默认的 CookieManager 来实现 Cookie 的自定义管理.
小结
(本文完 c-2-d e-20181005)
来源: https://www.cnblogs.com/throwable/p/9746384.html