# HTTP
# 1.三次握手
指建立一个 TCP 连接时,需要客户端和服务器总共发送 3 个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。
图中字符的含义:
SYN
:连接请求/接收 报文段seq
:发送的第一个字节的序号ACK
:确认报文段ack
:确认号。希望收到的下一个数据的第一个字节的序号
刚开始客户端处于 Closed
的状态,而服务端处于 Listen
状态:
CLOSED
:没有任何连接状态
LISTEN
:侦听来自远方 TCP 端口的连接请求
1)第一次握手:客户端向服务端发送一个 SYN 报文(SYN = 1),并指明客户端的初始化序列号 ISN(x),即图中的 seq = x,表示本报文段所发送的数据的第一个字节的序号。此时客户端处于 SYN_Send
状态。
SYN-SENT
:在发送连接请求后等待匹配的连接请求
2)第二次握手:服务器收到客户端的 SYN 报文之后,会发送 SYN 报文作为应答(SYN = 1),并且指定自己的初始化序列号 ISN(y),即图中的 seq = y。同时会把客户端的 ISN + 1 作为确认号 ack 的值,表示已经收到了客户端发来的的 SYN 报文,希望收到的下一个数据的第一个字节的序号是 x + 1,此时服务器处于 SYN_REVD
的状态。
SYN-RECEIVED
:在收到和发送一个连接请求后等待对连接请求的确认
3)第三次握手:客户端收到服务器端响应的 SYN 报文之后,会发送一个 ACK 报文,也是一样把服务器的 ISN + 1 作为 ack 的值,表示已经收到了服务端发来的的 SYN 报文,希望收到的下一个数据的第一个字节的序号是 y + 1,并指明此时客户端的序列号 seq = x + 1(初始为 seq = x,所以第二个报文段要 +1),此时客户端处于 Establised
状态。
服务器收到 ACK 报文之后,也处于 Establised 状态
,至此,双方建立起了 TCP 连接。
ESTABLISHED
:代表一个打开的连接,数据可以传送给用户
# 2.为什么要三次握手,两次不行吗?
最主要的目的就是双方确认自己与对方的发送与接收是正常的
只有经过三次握手才能确认双发的收发功能都正常,缺一不可:
第一次握手(客户端发送 SYN 报文给服务器,服务器接收该报文):
客户端:什么都不能确认;
服务器确认了:对方发送正常,自己接收正常
第二次握手(服务器响应 SYN 报文给客户端,客户端接收该报文):
客户端确认了:自己发送、接收正常,对方发送、接收正常;
服务器确认了:对方发送正常,自己接收正常
第三次握手(客户端发送 ACK 报文给服务器):
客户端确认了:自己发送、接收正常,对方发送、接收正常;
服务器确认了:自己发送、接收正常,对方发送、接收正常
# 3.ISN (Initial Sequence Number) 是固定的吗
三次握手的其中一个重要功能是客户端和服务端交换 ISN(Initial Sequence Number),以便让对方知道接下来接收数据的时候如何按序列号组装数据。
当一端为建立连接而发送它的 SYN 时,它会为连接选择一个初始序号。ISN 随时间而变化,因此每个连接都将具有不同的 ISN。如果 ISN 是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。
# 4.三次握手过程中可以携带数据吗
第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手绝对不可以携带数据
假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,然后疯狂重复发 SYN 报文的话(因为攻击者根本就不用管服务器的接收、发送能力是否正常,它就是要攻击你),这会让服务器花费很多时间、内存空间来接收这些报文。
# 5.什么是半连接队列
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD
状态,此时双方还没有完全建立其连接,服务器会把这种状态下的请求连接放在一个队列里,我们把这种队列称之为半连接队列。
当然还有一个全连接队列,完成三次握手后建立起的连接就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
# 6.如果第三次握手丢失了,客户端服务端会如何处理
服务器发送完 SYN-ACK 包,如果未收到客户端响应的确认包,也即第三次握手丢失。那么服务器就会进行首次重传,若等待一段时间仍未收到客户确认包,就进行第二次重传。如果重传次数超过系统规定的最大重传次数,则系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s…
# 7.SYN 攻击是什么?
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到 SYN 洪泛攻击。SYN 攻击就是 Client 在短时间内伪造大量不存在的 IP 地址,并向 Server 不断地发送 SYN 包,Server 则回复确认包,并等待 Client 确认,由于源地址不存在,因此 Server 需要不断重发直至超时,这些伪造的 SYN 包将长时间占用未连接队列,导致正常的 SYN 请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。
常见的防御 SYN 攻击的方法有如下几种:
- 缩短超时(SYN Timeout)时间
- 增加最大半连接数
- 过滤网关防护
- SYN cookies 技术
# 8.TCP 四次挥手
建立一个 TCP 连接需要三次握手,而终止一个 TCP 连接要经过四次挥手(也有将四次挥手叫做四次握手的)。这是由于 TCP 的半关闭(half-close)特性造成的,TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。
TCP 连接的释放需要发送四个包(执行四个步骤),因此称为四次挥手(Four-way handshake
),客户端或服务端均可主动发起挥手动作。
回顾一下上图中符号的意思:
FIN
:连接终止位seq
:发送的第一个字节的序号ACK
:确认报文段ack
:确认号。希望收到的下一个数据的第一个字节的序号
假设是客户端先发起关闭请求,四次挥手的过程如下:
简洁版:
客户端发送 FIN 报文给服务端
服务端收到后发送 ACK 报文给客户端
服务端发送 FIN 报文给客户端
客户端收到后,发送 ACK 给服务端,服务端关闭,客户端等待 2MSL 后关闭
详细版:
1)第一次挥手:客户端发送一个 FIN 报文(请求连接终止:FIN = 1),报文中会指定一个序列号 seq = u。并停止再发送数据,主动关闭 TCP 连接。此时客户端处于 FIN_WAIT1
状态,等待服务端的确认。
FIN-WAIT-1
- 等待远程 TCP 的连接中断请求,或先前的连接中断请求的确认;
2)第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT
状态。
CLOSE-WAIT
- 等待从本地用户发来的连接中断请求;
此时的 TCP 处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2
(终止等待 2)状态,等待服务端发出的连接释放报文段。
FIN-WAIT-2
- 从远程 TCP 等待连接中断请求;
3)第三次挥手:如果服务端也想断开连接了(没有要向客户端发出的数据),和客户端的第一次挥手一样,发送 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK
的状态,等待客户端的确认。
LAST-ACK
- 等待原来发向远程 TCP 的连接中断请求的确认;
4)第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答(ack = w+1),且把服务端的序列值 +1 作为自己 ACK 报文的序号值(seq=u+1),此时客户端处于 TIME_WAIT
(时间等待)状态。
TIME-WAIT
- 等待足够的时间以确保远程 TCP 接收到连接中断请求的确认;
🚨 注意 !!!这个时候由服务端到客户端的 TCP 连接并未释放掉,需要经过时间等待计时器设置的时间 2MSL(一个报文的来回时间) 后才会进入 CLOSED
状态(这样做的目的是确保服务端收到自己的 ACK 报文。如果服务端在规定时间内没有收到客户端发来的 ACK 报文的话,服务端会重新发送 FIN 报文给客户端,客户端再次收到 FIN 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文给服务端)。服务端收到 ACK 报文之后,就关闭连接了,处于 CLOSED
状态。
# 9.为什么要四次挥手
由于 TCP 的半关闭(half-close)特性,TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。
任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了 TCP 连接。
通俗的来说,两次握手就可以释放一端到另一端的 TCP 连接,完全释放连接一共需要四次握手。
举个例子:A 和 B 打电话,通话即将结束后,A 说 “我没啥要说的了”,B 回答 “我知道了”,于是 A 向 B 的连接释放了。但是 B 可能还会有要说的话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,于是 B 向 A 的连接释放了,这样整个通话就结束了。
# 10.四次挥手释放连接时,等待 2MSL(最长报文段寿命)的意义?
保证客户端发送的最后一个 ACK 报文段能够到达服务端。 这个 ACK 报文段有可能丢失,使得处于 LAST-ACK 状态的 B 收不到对已发送的 FIN+ACK 报文段的确认,服务端超时重传 FIN+ACK 报文段,而客户端能在 2MSL 时间内收到这个重传的 FIN+ACK 报文段,接着客户端重传一次确认,重新启动 2MSL 计时器,最后客户端和服务端都进入到 CLOSED 状态,若客户端在 TIME-WAIT 状态不等待一段时间,而是发送完 ACK 报文段后立即释放连接,则无法收到服务端重传的 FIN+ACK 报文段,所以不会再发送一次确认报文段,则服务端无法正常进入到 CLOSED 状态。
防止“已失效的连接请求报文段”出现在本连接中。 客户端在发送完最后一个 ACK 报文段后,再经过 2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。
# 11. 为什么握手只要三次,挥手却要四次?
关键就在中间两步。
- 建立连接时,当服务器收到客户端的
SYN 报文
后,可以直接发送SYNACK 报文
。其中ACK
是用来应答的,SYN
是用来同步的。 - 但是关闭连接时,当服务器收到
FIN 报文
时,很可能并不会立即关闭SOCKET
,所以只能先回复一个ACK 报文
,告诉客户端,“你发的FIN 报文
我收到了”。只有等到服务器所有的报文都发送/接收完了,我才能发送FIN 报文
,因此不能一起发送,需要四次握手。
# 12.说一说你对 DNS 的理解?
DNS (Domain Name System) 是互联网中的重要基础设施,负责对域名的解析工作,为了保证高可用、高并发和分布式,它设计成了树状的层次结构。
由根 DNS 服务器、顶级域 DNS 服务器和权威 DNS 服务器组成。
解析顺序是首先从浏览器缓存、操作系统缓存以及本地 DNS 缓存 (/etc/hosts) 逐级查找,然后从本地 DNS 服务器、根 DNS、顶级 DNS 以及权威 DNS层层递归查询。
可以基于域名在内网、外网进行负载均衡。
传统的 DNS 有很多问题(解析慢、更新不及时),HTTPDNS 通过客户端 SDK 和服务端配合,直接通过 HTTP 调用解析 DNS 的方式,可以绕过传统 DNS 这些缺点,实现智能调度
# 13.说一说你对 CDN 的理解?
CDN(Content Delivery Network) 就是内容分发网络。
为了突破现实生活中的光速、传输距离等物理限制,CDN 投入了大量资金,在全球范围内各大枢纽城市建立机房,部署大量高存储高带宽的节点,构建跨运营商、跨地域的专用高速传输网络。
DNS 分为中心节点、区域节点、边缘节点等,在用户接入网络后,首先通过全局负载均衡 (Global Sever Load Balance),简称 GSLB 算法负责调度,找到离用户最合适的节点。然后通过 HTTP 缓存代理技术进行缓存,缓存命中就返回给用户,否则就回源站去取。CDN 擅长缓存静态资源(图片、音频等),当然也支持动态内容的缓存。