文章目录
- 1.TCP
- 1.1 为什么需要 TCP 协议?TCP 工作在哪一层?
- 1.2 什么是 TCP ?
- 1.3 什么是 TCP 连接?
- 1.4 如何唯一确定一个 TCP 连接?
- 1.5 有一个 IP 的服务器监听了一个端口,它的 TCP 的最大连接数是多少?
- 1.6 UDP 和 TCP 有什么区别呢?分别的应用场景是?
- 1.7 为什么 UDP 头部没有「首部长度」字段,而 TCP 头部有「首部长度」字段呢?
- 1.8 为什么 UDP 头部有「包长度」字段,而 TCP 头部则没有「包长度」字段呢?
- 2.TCP 三次握手
- 2.1 如何在 Linux 系统中查看 TCP 状态?
- 2.2 为什么是三次握手?不是两次、四次?
- 2.3 为什么客户端和服务端的初始序列号 ISN 是不相同的?
- 2.4 初始序列号 ISN 是如何随机产生的?
- 2.5 既然 IP 层会分片,为什么 TCP 层还需要 MSS 呢?
- 2.6 什么是 SYN 攻击?如何避免 SYN 攻击?
- SYN 攻击
- 避免 SYN 攻击方式一
- 避免 SYN 攻击方式二
- 3.TCP 四次挥手
- 3.1 为什么挥手需要四次?
- 3.2 为什么 TIME_WAIT 等待的时间是 2MSL?
- 3.3 为什么需要 TIME_WAIT 状态?
- TIME_WAIT 过多有什么危害?
- 如果已经建立了连接,但是客户端突然出现故障了怎么办?
1.TCP
1.1 为什么需要 TCP 协议?TCP 工作在哪一层?
IP 层「不可靠」,它不提供服务质量的承诺,所传送的分组可能出错、丢失、重复、失序。它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。
如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。
因为 TCP 是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。
1.2 什么是 TCP ?
1.3 什么是 TCP 连接?
1.4 如何唯一确定一个 TCP 连接?
1.5 有一个 IP 的服务器监听了一个端口,它的 TCP 的最大连接数是多少?
1.6 UDP 和 TCP 有什么区别呢?分别的应用场景是?
1.7 为什么 UDP 头部没有「首部长度」字段,而 TCP 头部有「首部长度」字段呢?
1.8 为什么 UDP 头部有「包长度」字段,而 TCP 头部则没有「包长度」字段呢?
2.TCP 三次握手
- 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态手过程
- 客户发出连接建立请求报文段。A的TCP向B的TCP发出连接请求报文段。报文段首部中的同步位 SYN 置 1 ,同时把随机初始化序号 seq=x,表明下一个报文段中的第一个数据字节的序号是x+1,向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT状态 。
- 服务器发送确认报文段。B的TCP收到连接请求报文段后,如同意,发回确认。在确认报文段中,把同步位SYN和确认位ACK都置1,确认号是ack=x+1,同时也为自己选择一个初始序号seq=y。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。
- 客户发送确认段。A的TCP收到B的确认后,要向B给出确认,其确认位ACK置1,而自己的序号seq=x+1。TCP的标准规定,同步位SYN=1的报文段要消耗掉一个序号。因此,A发送的第二个报文段的序号应当是第一个报文段的序号加1。这次报文可以携带客户到服务器的数据,之后客户端处于 ESTABLISHED 状态。
- 服务器收到客户端的应答报文后,也进入 ESTABLISHED 状态。
- 从上面的过程可以发现第三次握手是可以携带数据的,前两次握手是不可以携带数据的。
- 运行客户进程的主机A的TCP通知上层应用进程,连接已经建立。
以后,当A向B发送第一个数据报文段时,序号位seq=x+1,因为前一个确认报文段并不消耗序号。
B收到A的确认报文后,也通知上层应用进程,连接已经建立。
2.1 如何在 Linux 系统中查看 TCP 状态?
2.2 为什么是三次握手?不是两次、四次?
TCP 连接:用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。
1.三次握手才可以阻止历史重复连接的初始化(主要原因)
客户端连续发送多次 SYN 建立连接的报文,在网络拥堵等情况下:
一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端;
那么此时服务端就会回一个 SYN + ACK 报文给客户端;
客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送 RST 报文给服务端,表示中止这一次连接。
如果是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可以在客户端(发送方)准备发送第三次报文时,客户端因有足够的上下文来判断当前连接是否是历史连接:
1) 如果是历史连接(序列号过期或超时),则第三次握手发送的报文是 RST 报文,以此中止历史连接;
2) 如果不是历史连接,则第三次发送的报文是 ACK 报文,通信双方就会成功建立连接;
所以, TCP 使用三次握手建立连接的最主要原因是防止历史连接初始化了连接。
2.三次握手才可以同步双方的初始序列号
TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素,它的作用:
1) 接收方可以去除重复的数据;
2) 接收方可以根据数据包的序列号按序接收;
3) 可以标识发送出去的数据包中,哪些是已经被对方收到的;
序列号在 TCP 连接中占据着非常重要的作用,所以当客户端发送携带「初始序列号」的 SYN 报文的时候,需要服务端回一个 ACK 应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。
- 四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了「三次握手」。
- 两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。
3.三次握手才可以避免资源浪费
- 如果只有「两次握手」,当客户端的 SYN 请求连接在网络中阻塞,客户端没有接收到 ACK 报文,就会重新发送 SYN ,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的 ACK 确认信号,所以每收到一个 SYN 就只能先主动建立一个连接,这会造成什么情况呢?
- 如果客户端的 SYN 阻塞了,重复发送多次 SYN 报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
- 即两次握手会造成消息滞留情况下,服务器重复接受无用的连接请求 SYN 报文,而造成重复分配资源。
不使用「两次握手」和「四次握手」的原因:
「两次握手」:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数.
2.3 为什么客户端和服务端的初始序列号 ISN 是不相同的?
因为网络中的报文会延迟、会复制重发、也有可能丢失,这样会造成不同连接之间产生互相影响,所以为了避免互相影响,客户端和服务端的初始序列号是随机且不同的。
2.4 初始序列号 ISN 是如何随机产生的?
起始 ISN 是基于时钟的,每 4 毫秒 + 1,转一圈要 4.55 个小时。
RFC1948 中提出了一个较好的初始化序列号 ISN 随机生成算法。
ISN = M + F (localhost, localport, remotehost, remoteport)
- M 是一个计时器,这个计时器每隔 4 毫秒加 1。
- F 是一个 Hash 算法,根据源 IP、目的 IP、源端口、目的端口生成一个随机数值。要保证 Hash 算法不能被外部轻易推算得出,用 MD5 算法是一个比较好的选择。
2.5 既然 IP 层会分片,为什么 TCP 层还需要 MSS 呢?
- MTU:一个网络包的最大长度,以太网中一般为 1500 字节;
MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度;
如果TCP 的整个报文(头部 + 数据)交给 IP 层进行分片,会有什么异常呢?
- 当 IP 层有一个超过 MTU 大小的数据(TCP 头部 + TCP 数据)要发送,那么 IP 层就要进行分片,把数据分片成若干片,保证每一个分片都小于 MTU。把一份 IP 数据报进行分片以后,由目标主机的 IP 层来进行重新组装后,在交给上一层 TCP 传输层。
- 这看起来井然有序,但这存在隐患的,那么当一个 IP 分片丢失,整个 IP 报文的所有分片都得重传。
- 因为 IP 层本身没有超时重传机制,它由传输层的 TCP 来负责超时和重传。
- 当接收方发现 TCP 报文(头部 + 数据)的某一片丢失后,则不会响应 ACK 给对方,那么发送方的 TCP 在超时后,就会重发「整个 TCP 报文(头部 + 数据)」。
因此,可以得知由 IP 层进行分片传输,是非常没有效率的。
- 所以,为了达到最佳的传输效能 TCP 协议在建立连接的时候通常要协商双方的 MSS 值,当 TCP 层发现数据超过 MSS 时,则就先会进行分片,当然由它形成的 IP 包的长度也就不会大于 MTU ,自然也就不用 IP 分片了。
- 经过 TCP 层分片后,如果一个 TCP 分片丢失后,进行重发时也是以 MSS 为单位,而不用重传所有的分片,大大增加了重传的效率。
2.6 什么是 SYN 攻击?如何避免 SYN 攻击?
SYN 攻击
我们都知道 TCP 连接建立是需要三次握手,假设攻击者短时间伪造不同 IP 地址的 SYN 报文,服务端每接收到一个 SYN 报文,就进入SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报文,无法得到未知 IP 主机的 ACK 应答,久而久之就会占满服务端的 SYN 接收队列(未连接队列),使得服务器不能为正常用户服务。
避免 SYN 攻击方式一
通过修改 Linux 内核参数,控制队列大小和当队列满时应做什么处理。
避免 SYN 攻击方式二
Linux 内核的 SYN (未完成连接建立)队列与 Accpet (已完成连接建立)队列是工作流程:
3.TCP 四次挥手
双方都可以主动断开连接,断开连接后主机中的「资源」将被释放。
3.1 为什么挥手需要四次?
- 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
- 服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。
从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发送,从而比三次握手导致多了一次。
3.2 为什么 TIME_WAIT 等待的时间是 2MSL?
- MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个 TTL 字段,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。
MSL 与 TTL 的区别:MSL 的单位是时间,而 TTL 是经过路由跳数。所以 MSL 应该要大于等于 TTL 消耗为 0 的时间,以确保报文已被自然消亡。
- TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是:网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间。
- 如果被动关闭方没有收到断开连接的最后的 ACK 报文,就会触发超时重发 Fin 报文,另一方接收到 FIN 后,会重发 ACK 给被动关闭方, 一来一去正好 2 个 MSL。
- 2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。
3.3 为什么需要 TIME_WAIT 状态?
原因一:防止旧连接的数据包
假设 TIME-WAIT 没有等待时间或时间过短,被延迟的数据包抵达后会发生什么呢?
原因二:保证连接正确关闭
TIME-WAIT 作用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。
假设 TIME-WAIT 没有等待时间或时间过短,断开连接会造成什么问题呢?
TIME_WAIT 过多有什么危害?
如果已经建立了连接,但是客户端突然出现故障了怎么办?