HTTP详解

HTTP协议是“看不着、摸不着”的。因为在Java语言中,有太多的HTTP相关的库,如:HTTPClient、HTTPUrlConnection、Spring的RestTemplete等。因此在某种程度上从事Java相关的开发人员很难需要去深扒HTTP协议,而基本上都属于会用就好的状态。

编写本文的思路正如下面的文章目录。

目录

  • 一、HTTP简介
  • 二、什么是协议
    • 2.1 协议的定义
  • 三、Wireshark网络抓包工具
  • 四、HTTP协议
    • 4.1 前置知识
    • 4.2 请求协议结构
      • 4.2.1 描述
      • 4.2.2 抓包查看
    • 4.3 响应协议结构
      • 4.3.1 描述
      • 4.3.2 抓包查看
    • 4.4 分包传输
      • 4.4.1 结构图
      • 4.4.2 优点
      • 4.4.3 缺点
      • 4.4.4 抓包查看
    • 4.5 KeepAlive
      • 4.5.1 Tomcat对KeepAlive的支持
      • 4.5.2 TCP协议与HTTP协议中的KeepAlive区别
      • 4.5.3 如何复用长连接
      • 4.5.4 如何关闭长链接
      • 4.5.5 疑问待解决
      • 4.5.6 4.4 分包传输
  • 五、HTTP协议如何解决TCP的粘包与拆包

一、HTTP简介

HTTP中文名称是超文本传输协议(英文:HyperText Transfer Protocol),HTTP是基于 TCP/IP 协议之上的应用层协议,HTTP是万维网数据通信的基础。

因此,在HTTP建立之前,需要先等客户端与服务端建立TCP连接。

二、什么是协议

在了解具体的HTTP协议之前,最好还是先了解什么叫协议,这对之后的学习其它协议肯定会有帮助。

2.1 协议的定义

协议,网络协议的简称,网络协议是通信计算机双方必须共同遵从的一组约定。如怎么样建立连接、怎么样互相识别等。只有遵守这个约定,计算机之间才能相互通信交流。它的三要素是:语法、语义、时序。

为了使数据在网络上从源到达目的,网路通信的参与方必须遵循相同的规则,这套规则称为协议(protocol),它最终体现在网络上传输的数据包的格式。

协议往往分为几个层次进行定义,分层定义是为了使某一层协议的改变不影响其他层次的协议。

备注:以上文字来源于百度百科

三、Wireshark网络抓包工具

Wireshark是一个网络封包分析软件。网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料。Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换。

在学习HTTP协议的过程中,因为HTTP协议的是看不见摸不着的,这给学习带来一定的难度。因此有必要用Wireshark抓包工具抓取一个HTTP协议下来观察。

Wireshark官网地址:https://wireshark.en.softonic.com/
先看一下Wireshark的界面:

1.Wireshark1.png

显而易见:Wireshark抓包界面中分为顶部的菜单栏、过滤栏、上、中、下五个部分;

  • 过滤栏:用于写一些过滤条件,避免被抓的包太多,导致眼花缭乱不方便观察包
  • 上:请求列表,包含TCP、HTTP等等协议包
  • 中:一共有5个Item。从上到下分别为:物理层、数据链路层、网络层、传输层、应用层。有时候还会多一个Item,多的Item只是Wireshark将应用层中携带的数据解析出来展示而已,网络包中实际没有。因此这一区域的数据,Wireshark主要是用来展示更加人性化、经过加工后的数据。
  • 下:十六进制数据,是真正网络包中的数据;

四、HTTP协议

4.1 前置知识

在HTTP协议中,有很多的符号如:\n\r等,而也正是因为这些符号对HTTP协议解决拆包、粘包提供了基础

标识 ASCII 描述 字符
CR 13 回车 \n
LF 10 换行 \r
SP 32 空格
COLON 58 冒号 :

4.2 请求协议结构

4.2.1 描述

HTTP请求体中包含三个部分:① 请求行、② 请求头(Header)、③ 请求正文(Body)

0.HTTP协议Request透视图.png

概括为文字如下:

  • 请求行:

    包含三个部分:Method、URL、协议/版本,而这之前用空格隔开,在请求行的最后添加CRLF

  • 请求头:

    包含若干个键值对(格式为:key:value),在每个键值对之间都用CRLF隔开,在整个请求头的最后再加上一个CRLF;也就是说最后一个键值对的最后会有两个CRLF。

  • 请求正文:

    请求正文中主要是POST Method提交的数据,数据的格式可以有很多种,具体什么格式实在请求头中由Content-Type定义。在不考虑分包传输的情况下,请求正文中数据的字节数由Content-Length指定。

4.2.2 抓包查看

2.Wireshark2.png

4.3 响应协议结构

4.3.1 描述

HTTP响应体中包含三个部分:① 状态行、② 响应头(Header)、③ 响应正文(Body)

3.HTTP协议Response透视图.png

概括为文字如下:

  • 响应行:

    包含三个部分:协议/版本、响应状态码、状态码描述符,而这之前用空格隔开,在请求行的最后添加CRLF

  • 响应头:

    不说了,响应头格式同Request中的请求头格式

  • 响应正文:

    不说了,响应正文同Request中的请求正文

4.3.2 抓包查看

4.Wireshark4.png

看到这里,有的小伙伴可能会提出问题:

问:在上图中[响应头]与[响应正文]之间是什么数据呢?

答:这些数据是Wireshark为了辅助展示出的一些指标数据。实际上,在网络包中并没有这些数据,这里对比一下最下面的方块,看一下网络包中真正的数据。

问:在网络分层中,Hypertext Transfer Protocol已经是网络模型中的应用层了,那为什么上图的抓包中,最下面还有一层:Line-base text data:text/plain(1 lines)

答:这个问题的答案其实和上一个问题的答案一致,这只是Wireshark为了展示的更加人性化,将数据抽离在了该层展示低而已,注意看最下面的红色框框,在Header结束之后就是两个CRLF,紧接着就是数据(hello world!)了。

4.4 分包传输

在HTTP协议中,除了将数据放在一个包中一次性发出,还可以通过分包方式将一个大批数据分在多个数据包中进行传输。需要注意的是,分包传输使用的也是同一个Socket连接。
在Header中通过Transfer-Encoding: chunked进行指定,该KV与Content-Length只能同时出现一个。
需要注意的是:在HTTP1.0版本协议中不支持分包。在HTTP1.1之后开始支持分包。

4.4.1 结构图

5.chunked.png

4.4.2 优点

  • 当响应数据体量大时,避免浏览器出现忙等导致页面出现长时间的空白
  • 当要发送的数据长度很难计算时,为了保证吞吐量,可以先将一部分数据通过一个子包先发出去

4.4.3 缺点

  • 协议比较复杂

4.4.4 抓包查看

6.Wireshark6.png

4.5 KeepAlive

HTTP协议如果不做特殊处理都是一种短连接,即为一次会话建立TCP/IP之后,客户端与服务端基于TCP/IP的连接之上通过HTTP协议完成会话后,连接也会被服务端断开。下一次的HTTP请求,还是需要为这个请求进行三次握手创建TCP连接,用完之后还需要四次挥手断开TCP连接,这大大减少了传输效率。

而KeepAlive的产生就是为了弥补上述的不足。

HTTP1.0版本中需要在Header中加入Connection: keep-alive来指定会话完毕之后不立即断开连接,而是有一定的复用时间。HTTP1.1版本中,默认开启该配置:Connection: keep-alive

4.5.1 Tomcat对KeepAlive的支持

  • KeepAliveTimeout

    意义在于当请求结束之后默认要等待该值的时长,以达到复用的效果。

    在Tomcat9中,该参数值默认等于ConnectionTimeout的时长,而ConnectionTimeout在server.xml中默认是20000ms。ConnectionTimeout的含义在于:当连接建立起之后,可以忍受多久的时间客户端没有发数据过来。

    看一段Tomcat9的源码:

    
      

    The number of milliseconds this Connector will wait for another HTTP request before closing the connection. The default value is to use the value that has been set for the connectionTimeout attribute. Use a value of -1 to indicate no (i.e. infinite) timeout.

    但是需要注意,该值最好别设置的太久,当大流量打入之后可能会导致资源始终无法释放。

    这里有一篇线上问题的复盘博客,可以参考一下:

    http://www.voycn.com/article/tomcat-connectiontimeout-lijie

  • MaxKeepAliveRequests

    Tomcat某个时刻最大能维护多少的长链接,这个值在Tomcat9版本中默认为100。

    看一段Tomcat9的源码:

    
      

    The maximum number of HTTP requests which can be pipelined until the connection is closed by the server. Setting this attribute to 1 will disable HTTP/1.0 keep-alive, as well as HTTP/1.1 keep-alive and pipelining. Setting this to -1 will allow an unlimited amount of pipelined or keep-alive HTTP requests. If not specified, this attribute is set to 100.

4.5.2 TCP协议与HTTP协议中的KeepAlive区别

需要注意的是:TCP协议中也有KeepAlive参数,但是与HTTP的KeepAlive还是有区别的;

  • TCP-KeepAlive

    TCP的KeepAlive是为了连接保活,该机制有三个重要的参数:

    tcp_keepalive_intvl:保活探测消息的发送频率

    tcp_keepalive_probes:需要发送X次的探测消息但依然无效,则认为连接断开

    tcp_keepalive_time:最后一次数据交换到TCP第一次发送探针消息的间隔时间

  • HTTP-Keep-Alive

    HTTP发出响应之后不要断开连接,连接需要保持一段时间,以达到复用的效果。

4.5.3 如何复用长连接

首先需要了解的是,复用到底复用的是什么,看了上面的介绍,短连接中的每次请求都需要先进行TCP的连接导致TCP链路无法复用,因此这里的复用其实是针对TCP链路的复用。

可以配合连接池,如HttpClient就对长连接具有管理的功能,注意这里的管理肯定是针对长连接了,短连接压根就无法复用。

4.5.4 如何关闭长链接

当TCP连接需要关闭时,只需要在Header头中添加Connection: close即可。

4.5.5 疑问待解决

Tomcat9版本中默认KeepTimeout为20s,我也查看了一下SpringBoot对于Tomcat的配置,发现SpringBoot没有对Tomcat参数做调整,至少KeepTimeout用的就是Tomcat默认值。

那么在这样的情况下,KeepTimeout最终应该为20s才对,但是在抓包过程中却发现其值为60s.

抓包如下:


7.Wireshark7.jpg

4.5.6 从JVM层面判断连接是否复用

TODO

五、HTTP协议如何解决TCP的粘包与拆包

TCP协议,数据传输都是stream式的,数据之间是没有流边界的,在这样情况下自然就会出现粘包与拆包的问题。那么基于TCP之上的HTTP协议当然也会面临这样的问题,那它是如何解决的呢?

了解了HTTP协议的结构之后,解决粘包拆包的问题就迎刃而解了。

HTTP的请求与响应,对于请求行、请求头、响应行、响应头而言,都可以通过CRLF与空格作为流的边界进行读取。

对于请求正文与响应正文而言,他们的字节长度在Header中的Content-Length中定义,假若,Header中没有Content-Length属性,取而代之的是Transfer-Encoding: chunked,那么就读取每一个chunk的size,继而再读取size个byte,知道读取到last-chunk的size为0。

你可能感兴趣的:(HTTP详解)