tcp, http, http2的一些理解

http短链接

  • 为何Http是无状态的短链接呢?而TCP是有状态的长链接?Http不是创建在TCP的基础上吗,为何还能是短链接?如今明白了,Http就是在每次请求完成后就把TCP链接关了,因此是短链接。而咱们直接经过Socket编程使用TCP协议的时候,由于咱们本身能够经过代码区控制何时打开链接何时关闭链接,只要咱们不经过代码把链接关闭,这个链接就会在客户端和服务端的进程中一直存在,相关状态数据会一直保存着
  • HTTP 1.0 初衷主要是解决WEB文档在网络中的传输问题,由于传输文件是一个低频的请求,不必进行长时间链接,因此HTPP 1.0 被设计成短链接,每进行一次HTPP通讯后就会断开TCP链接。
    HTTP 1.1版本随着互联网的发展,HTTP 再也不只是传送简单的文件信息,多样化的文本信息开始普遍应用,像html 这样的网页访问的同时会同时附带很是多的图片之类的信息,若是每一个请求都要进行TCP链接和断开(三次握手和四次挥手),这样势必会形成不少额外的通讯开销。
    因此为了解决此问题 HTTP 协议1.1版本会在请求的时候,只要任意一端没有明确的提出断开链接则保持TCP链接状态。

socket是什么

  • 套接字(socket)是通讯的基石,是支持TCP/IP协议的网络通讯的基本操做单元。它是网络通讯过程当中端点的抽象表示,包含进行网络通讯必须的五种信息:链接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

三次握手

image.png

  • 第一次握手:

客户主机发起链接请求,设置SYN标志位为1,同时客户端随机选择了一个初始序号client_isn,而且存放在TCP报文字段的序号
image.pngcss

  • 第二次握手:

接下来,当服务端接收到该报文后,会为其分配TCP 缓存和变量(这使得TCP容易受到被称为SYN 洪泛攻击的拒绝服务攻击)紧接着,服务端会返回一个SYNACK 报文到客户端,其中SYN标志位为1确认号设置为client_isn + 1,而且选一个本身的初始序号server_isn,并放置在序号字段中
image.pnghtml

  • 第三次握手:

当收到服务器发来的SYNACK报文段后,客户端也须要给该链接分配缓存和变量,而后再次发送一个确认报文给服务端,其中,SYN标志位设置为0,将确认号设置为server_isn + 1,另外,这次报文能够携带负载数据
image.pngjava

问题

为何要三次握手而不是两次?

简单来讲,三次握手的目的是为了让双方验证各自的接收能力和发送能力算法

  • 第一次握手,A 发送SYNBB接收到了后,能确认什么呢? 显然,B能确认A发送能力和B接收能力;
  • 第二次握手,B发送SYNACKAA接收到后,能确认什么呢? A能确认B的发送能力和A本身的接收能力,此外,A收到了SYNACK,那么说明前面A发的SYN成功到达B的手中,因此也能确认A本身的发送能力和B接收能力;至此,A已经确认了双方各自的发送能力和接收能力都是OK的,所以转为ESTABLISHED状态;
  • 第三次握手,A发送ACKBB接收后,能确认什么呢?
    直接的,B能确认A发送能力和B接收能力,另外因为B能收到ACK说明前面发送的SYNACK已经成功被接受了,说明能确认A接收能力和B发送能力。

若是使用两次握手,就不能确认上述所说的四种能力,那么就会致使问题。编程

假定不采用第三次报文握手,那么只要B发出确认,新的链接就创建了。浏览器

现假定一种异常状况,即A发出的SYN报文段并无丢失,而是在某些网络节点长时间滞留了,以至延误到链接释放后的某个时间才到达B。原本这是一个早已失效的报文段。但B收到此失效的链接请求报文段后,却误觉得是A又发出一次新的链接请求,因而就向A发出确认报文段,赞成创建链接。缓存

因为如今A并无发出创建链接的请求,所以不会理睬B的确认,也不会向B发送数据,但B却觉得新的运输链接已经创建了,并一直等待A发来的数据。B的许多资源就这样白白浪费了。服务器

ACK报文丢失致使第三次握手失败网络

当客户端收到服务端的SYNACK应答后,其状态变为ESTABLISHED,并会发送ACK包给服务端,准备发送数据了。若是此时ACK在网络中丢失(如上图所示),过了超时计时器后,那么服务端会从新发送SYNACK包,重传次数根据/proc/sys/net/ipv4/tcp_synack_retries来指定,默认是5次。若是重传指定次数到了后,仍然未收到ACK应答,那么一段时间后,Server自动关闭这个链接。并发

问题就在这里,客户端已经认为链接创建,而服务端则可能处在SYN-RCVD或者CLOSED,接下来咱们须要考虑这两种状况下服务端的应答:

  • 服务端处于CLOSED,当接收到链接已经关闭的请求时,服务端会返回RST 报文,客户端接收到后就会关闭链接,若是须要的话则会重连,那么那就是另外一个三次握手了。
  • 服务端处于SYN-RCVD,此时若是接收到正常的ACK 报文,那么很好,链接恢复,继续传输数据;若是接收到写入数据等请求呢?注意了,此时写入数据等请求也是带着ACK 报文的,实际上也能恢复链接,使服务器恢复到ESTABLISHED状态,继续传输数据。

(ISN)是固定的吗?

不固定,client_isn是随机生成的,而server_isn则须要根据SYN 报文中的源、ip和端口,加上服务器自己的密码数进行相同的散列获得,显然这也不是固定的。

三次握手过程当中能够携带数据吗?

第三次握手是能够携带数据的,而前两次不行。

四次挥手

image.png
首先,当前客户端和服务器的状态都为ESTABLISHED

  • 第一次挥手

客户主机发起链接释放的请求,设置FIN1,固然,序号seq也会带上,这里假设为u;发送完毕后,客户端进入 FIN-WAIT-1 状态
image.png

  • 第二次挥手

服务端接收到FIN 报文后,会返回一个ACK 报文回去,此时设置ACK1确认号u + 1;代表本身接受到了客户端关闭链接的请求,但尚未准备好关闭链接。发送完毕后,服务器端进入 CLOSE-WAIT 状态,客户端接收到这个确认包以后,进入 FIN-WAIT-2 状态,等待服务器端关闭链接
image.png

  • 第三次挥手

服务器端准备好关闭链接时,向客户端发送结束链接请求,FIN 置为1;发送完毕后,服务器端进入 LAST-ACK 状态,等待来自客户端的最后一个ACK
image.png

  • 第四次挥手

客户端接收到服务端传来的FIN 报文后,知道服务器已经准备好关闭了,发送一个确认包,并进入 TIME-WAIT状态,等待可能出现的要求重传的ACK 报文;服务器端接收到这个确认包以后,关闭链接,进入 CLOSED 状态。
客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)以后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭链接,因而本身也关闭链接,进入 CLOSED 状态
image.png

四次挥手重要的是TIME-WAIT状态,为何须要这个状态呢?

要确保服务器是否已经收到了咱们的ACK 报文,若是没有收到的话,服务器会从新发FIN 报文给客户端,那么客户端再次收到FIN 报文以后,就知道以前的 ACK 报文丢失了,就会再次发送ACK 报文

为何握手只要三次,挥手却要四次?

关键就在中间两步。

  • 创建链接时,当服务器收到客户端的SYN 报文后,能够直接发送SYNACK 报文。其中ACK是用来应答的,SYN是用来同步的。
  • 可是关闭链接时,当服务器收到FIN 报文时,极可能并不会当即关闭SOCKET,因此只能先回复一个ACK 报文,告诉客户端,“你发的FIN 报文我收到了”。只有等到服务器全部的报文都发送/接收完了,我才能发送FIN 报文,所以不能一块儿发送,须要四次握手

为何 TIME_WAIT 状态须要通过 2MSL 才能转换到 CLOSE 状态?

  • 第一,为了保证客户端发送的最后一个ACK 报文可以到达服务器。咱们必须假设网络是不可靠的,ACK 报文可能丢失。若是服务端发出FIN 报文后没有收到ACK 报文,就会重发FIN 报文,此时处于TIME-WAIT状态的客户端就会重发ACK 报文。固然,客户端也不能无限久的等待这个可能存在的FIN 报文,由于若是服务端正常接收到了ACK 报文后是不会再发FIN 报文的。所以,客户端须要设置一个计时器,那么等待多久最合适呢?所谓的MSL(Maximum Segment Lifetime)指一个报文在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。若是直到2MSL时间后,客户端都没有再次收到FIN 报文,那么客户端推断ACK 报文已经被服务器成功接收,因此结束TCP 链接
  • 第二,防止已失效的链接请求报文段出如今新的链接中。客户端在发送完最后一个ACK 报文后,再通过时间2MSL,就可使因为网络不通畅产生的滞留报文段失效。这样下一个新的链接中就不会出现旧的链接请求报文

HTTP链接方式的进化史:

HTTP/0.9时代:短链接

每一个HTTP请求都要经历一次DNS解析、三次握手、传输和四次挥手。反复建立和断开TCP链接的开销巨大,在如今看来,这种传输方式简直是糟糕透顶。

HTTP/1.0时代:持久链接概念提出

人们认识到短链接的弊端,提出了持久链接的概念,在HTTP/1.0中获得了初步的支持。
持久链接,即一个TCP链接服务屡次请求:
客户端在请求header中携带Connection:
Keep-Alive,便是在向服务端请求持久链接。若是服务端接受持久链接,则会在响应header中一样携带Connection:
Keep-Alive,这样客户端便会继续使用同一个TCP链接发送接下来的若干请求。(Keep-Alive的默认参数是[timout=5,
max=100],即一个TCP链接能够服务至多5秒内的100次请求)

当服务端主动切断一个持久链接时(或服务端不支持持久链接),则会在header中携带Connection: Close,要求客户端中止使用这一链接。

HTTP/1.1时代:持久链接成为默认的链接方式;提出pipelining概念

HTTP/1.1开始,即便请求header中没有携带Connection: Keep-Alive,传输也会默认以持久链接的方式进行。
目前全部的浏览器都默认请求持久链接,几乎全部的HTTP服务端也都默认开启对持久链接的支持,短链接正式成为过去式。(HTTP/1.1的发布时间是1997年,最后一次对协议的补充是在1999年,咱们能够夸张地说:HTTP短链接这个概念已通过时了近20年了。)
同时,持久链接的弊端被提出 —— HOLB(Head of Line Blocking)
即持久链接下一个链接中的请求仍然是串行的,若是某个请求出现网络阻塞等问题,会致使同一条链接上的后续请求被阻塞。

因此HTTP/1.1中提出了pipelining概念,即客户端能够在一个请求发送完成后不等待响应便直接发起第二个请求,服务端在返回响应时会按请求到达的顺序依次返回,这样就极大地下降了延迟。

然而pipelining并无完全解决HOLB,为了让同一个链接中的多个响应可以和多个请求匹配上,响应仍然是按请求的顺序串行返回的。因此pipelining并无被普遍接受,几乎全部代理服务都不支持pipelining,部分浏览器不支持pipelining,支持的大部分也会将其默认关闭。

SPDY和HTTP/2:multiplexing

multiplexing即多路复用,在SPDY中提出,同时也在HTTP/2中实现。
multiplexing技术可以让多个请求和响应的传输彻底混杂在一块儿进行,经过streamId来互相区别。这完全解决了holb问题,同时还容许给每一个请求设置优先级,服务端会先响应优先级高的请求。

如今Chrome、FireFox、Opera、IE、Safari的最新版本都支持SPDY,Nginx/Apache HTTPD/Jetty/Tomcat等服务端也都提供了对SPDY的支持。

http2

image.png

  • HTTP/1里的header对应HTTP/2里的 HEADERS frame
  • HTTP/1里的payload对应HTTP/2里的 DATA frame

下面从一个真实的gRPC SayHello请求,查看它在HTTP/2上是怎样实现的
image.png

能够看到下面这些Header:

  • Header: :authority: localhost:50051
  • Header: :path: /helloworld.Greeter/SayHello
  • Header: :method: POST
  • Header: :scheme: http
  • Header: content-type: application/grpc
  • Header: user-agent: grpc-java-netty/1.11.0

而后请求的参数在DATA frame里:

  • GRPC Message: /helloworld.Greeter/SayHello, Request

简而言之,gGRPC把元数据放到HTTP/2 Headers里,请求参数序列化以后放到 DATA frame里

HTTP / 2 主要有两个规范组成

  • Hypertext Transfer Protocol version 2 (超文本传输协议版本 2)
  • HPACK - HTTP / 2 的头压缩 (HPACK 是一种头部压缩算法)

HTTP2 的特性

HTTP / 2 支持 HTTP / 1.1 的全部核心功能,但旨在经过多种方式提升效率

  • HTTP/2 采用二进制传输数据,而非 HTTP/1 的文本格式传输
  • HTTP / 2 基本协议单元是帧,好比 head(头部信息)帧,data(传输数据细信息)帧
  • HTTP / 2 使用流技术支持多路复用,也就是说提供了在单个链接上复用 HTTP 请求和响应的能力, 多个请求或响应能够同时在一个链接上使用流.
  • HTTP / 2 支持压缩头部帧,容许将多个请求压缩成成一个分组,并且在客户端和服务器端分别头部信息创建索引,相同的表头只须要传输索引就能够。
  • HTTP / 2 支持对请求划分优先级(就是流的优先级)
  • HTTP / 2 支持 Server Push 技术

HTTP2 的原理

多路复用

HTTP/2 将每个请求变成流,每个流都有本身的 ID,有本身的优先级,这些流能够由客户端发送到服务端,也能够由服务端发送到客户端,将数据划分为帧,头部信息为 head 帧,实体信息为 data 帧,最后将这些流乱序发送到一个 TCP 链接中,以下图:
image.png

  • HTTP/2 中,在一个浏览器同域名下的全部请求都是在单个链接中完成,这个链接能够承载任意数量的双向数据流,每一个数据流都以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间能够乱序发送,根据帧首部的流标识能够将多个帧从新组装成一个流。
  • 在 HTTP/1 中,若是想并发发送多个请求,必须建立多个 TCP 链接,并且浏览器为了减小负载,会对同一域名下的 TCP 链接作限制,这样当请求量比较大时,会引发阻塞

服务器推送

  • HTTP /1 中客户端往服务端发送请求严格遵照一个请求,一个响应,好比客户端请求展现网页时,服务端发挥 HTML 内容,浏览器解析时发送 css,js 请求,服务端又返回 css,js 文件,那么服务端为何不能在返回网页时就推送 css,js 内容给客户端呢,在 HTTP /2 中这已功能已经支持,
  • 服务端主动推送也会遵照同源策略,不会随便推送第三方的资源到客户端

若是服务端推送资源是呗客户端缓存过的,客户端是有权力拒绝服务端的推送的,浏览器能够经过发送 RST_STREAM 帧来拒收。

  • 每个服务端推送的资源都是一个流

头部压缩

HTTP /1 的请求头较大,并且是以纯文本发送,HTTP/2 对消息头进行了压缩,采用的是 HACK 算法,可以节省消息头占用的网络流量,其主要是在两端创建了索引表,消息头在传输时能够采用索引,而 HTTP/1.x 每次请求,都会携带大量冗余头信息,浪费了不少带宽资源

grpc

gRPC 是 Google 基于 HTTP/2 以及 protobuf 的,要了解 gRPC 协议,只须要知道 gRPC 是如何在 HTTP/2 上面传输就能够了。

gRPC 一般有四种模式,unary,client streaming,server streaming 以及 bidirectional streaming,对于底层 HTTP/2 来讲,它们都是 stream,而且仍然是一套 request + response 模型。

Request

gRPC 的 request 一般包含

  • Request-Headers
  • 0 或者多个 Length-Prefixed-Message
  • EOS
  1. Request-Headers 直接使用的 HTTP/2 headers,在 HEADERS 和 CONTINUATION frame 里面派发。定义的 header 主要有 Call-Definition 以及 Custom-Metadata。Call-Definition 里面包括 Method(其实就是用的 HTTP/2 的 POST),Content-Type 等。而 Custom-Metadata 则是应用层自定义的任意 key-value,key 不建议使用 grpc- 开头,由于这是为 gRPC 后续本身保留的。
  2. Length-Prefixed-Message 主要在 DATA frame 里面派发,它有一个 Compressed flag 用来表示改 message 是否压缩,若是为 1,表示该 message 采用了压缩,而压缩算法定义在 header 里面的 Message-Encoding 里面。而后后面跟着四字节的 message length 以及实际的 message。
  3. EOS(end-of-stream) 会在最后的 DATA frame 里面带上了 END_STREAM 这个 flag。用来表示 stream 不会在发送任何数据,能够关闭了。

Response

Response 主要包含

  • Response-Headers
  • 0 或者多个 Length-Prefixed-Message
  • Trailers,若是遇到了错误,也能够直接返回 Trailers-Only。
  1. Response-Headers 主要包括 HTTP-Status,Content-Type 以及 Custom-Metadata 等。
  2. Trailers-Only 也有 HTTP-Status ,Content-Type 和 Trailers。
  3. Trailers 包括了 Status 以及 0 或者多个 Custom-Metadata。

HTTP-Status 就是咱们一般的 HTTP 200,301,400 这些,很通用就再也不解释。Status 也就是 gRPC 的 status, 而 Status-Message 则是 gRPC 的 message。Status-Message 采用了 Percent-Encoded 的编码方式,具体参考这里

若是在最后收到的 HEADERS frame 里面,带上了 Trailers,而且有 END_STREAM 这个 flag,那么就意味着 response 的 EOS。

Protobuf

gRPC 的 service 接口是基于 protobuf 定义的,咱们能够很是方便的将 service 与 HTTP/2 关联起来。

  • Path : /Service-Name/{method name}
  • Service-Name : ?( {proto package name} "." ) {service name}
  • Message-Type : {fully qualified proto message name}
  • Content-Type : "application/grpc+proto"

grpc的好处

  1. 跨语言,protobuf
  2. 性能好,基于http2协议实现的,http2协议提供了不少新的特性,而且在性能上也比http1提升了许多
  3. 标准化状态码
  4. 负载均衡,服务发现,日志,监控等都支持可插拔机制

四类服务方法

基于 http2 协议的特性:gRPC 容许定义以下四类服务方法

  • 单项 RPC:客户端发送一次请求,等待服务端响应结构,会话结束,就像一次普通的函数调用这样简单
  • 服务端流式 RPC:客户端发起一块儿请求,服务端会返回一个流,客户端会从流中读取一系列消息,直到没有结果为止
  • 客户端流式 RPC:客户端提供一个数据流并写入消息发给服务端,一旦客户端发送完毕,就等待服务器读取这些消息并返回应答
  • 双向流式 RPC:客户端和服务端都一个数据流,均可以经过各自的流进行读写数据,这两个流是相互独立的,客户端和服务端均可以按其但愿的任意顺序独写

protobuf的原理

  • Base 128 varint

这是一个编码算法,咱们都知道,int32 占四个字节,int64 占 8 个字节,这是固定的,无论这个数字是 1 仍是 123456,占的字节数是同样,那有没有一种能根据数字大小变长编码的算法呢?Base 128 varint 就是,在设置二进制网络协议通讯时,这种好处是可观的,可以带来性能上的提高。为何叫 128 呢,就是由于采用 7bit 的空间存储数据(一个字节占 8bit,但只采用 7bit),7bit 最大固然只能存储 128 了,那么最高位干啥呢?最高位用来看成一个标识 (flag), 若是最高位是 0 就表示这个最后一个字节了。

示例:

咱们用一个数字 10 和数字 300 来说解一下上面的 Base 128 varint

先说数字 10,转化为二进制后是:0000 1010,为何只有八位呢,由于 10 用一个字节表示已经足够了,最高位为 0(加粗的那个),表示这是最后一个字节了,不须要再用额外的字节来存储了

再来看数字 300,转化为二进制后是:‭‭00010010_1100‬, 转化成 varint,以下步骤:

按照 7 位进行分开, 0000010_0101100,不够的补 0
进行反转:0101100_0000010
最高位补数,第一个字节最高位补 1,第二个字节最高位补 0:10101100_00000010

ProtoBuffer 序列化后的存储格式

Tag,Length,Value ,这是序列化后存储的二进制的格式,Tag 你们简单理解为就是 proto 文件中字段后面的编号,Length 是这个字段对应的值的字节长度,Value 就是具体的值了,最终将全部数据拼装成一个流,以下图:
image.png
由图咱们得知,ProtoBuffer 存储是紧密的,各个字段很是紧凑,不会浪费空间,若某个字段没有赋值,则不会出如今序列化后的数据中,相应字段在解码时才会被设置默认值
image.png

T 表明的 tag 是由 fieldNumber(字段编号)和 wireType(上图中最左边的 0,1,2...)组成的,fieldNumber 保证了字段不重复和他在数据流中的位置,wireType 标记了数据类型,若是是 varint 编码,fieldNumber 也保证了数据字节的长度 (L)

参考文章

https://www.zhihu.com/search?...
https://zhuanlan.zhihu.com/p/...
https://learnku.com/articles/...

相关文章
相关标签/搜索