一、TCP 基本认识
1.1 瞧瞧TCP格式
序列号:在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
确认应答号:指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决不丢包的问题。
控制位:
ACK:该位为 1 时,「确认应答」的字段变为有效。
RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接
SYN:该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定
FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。
1.2 为什么需要 TCP 协议? TCP 工作在哪一层?
IP 层是 「不可靠」 的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性
如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。
因为 TCP 是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。
1.3 什么是 TCP ?
TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。
面向连接:一定是 「一对一」进行连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的。
可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端;
字节流:消息是「没有边界」的,所以无论我们消息有多大都可以进行传输。并且消息是「有序的」,当
「前一个」消息没有收到的时候,即使它先收到了后面的字节,那么也不能扔给应用层去处理,同时「重复」的报文会自动丢弃。
1.4 什么是 TCP 连接?
简单来说就是,用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接
所以我们可以知道,建立一个 TCP 连接是需要客户端与服务器端达成上述三个信息的共识。
Socket:由 IP 地址和端口号组成
序列号:用来解决乱序问题等
窗口大小:用来做流量控制
1.5 如何唯一确定一个 TCP 连接呢?
TCP 四元组可以唯一的确定一个连接,四元组包括如下:
源地址
源端口
目的地址
目的端口
源地址和目的地址的字段(32位)是在 IP 头部中,作用是通过 IP 协议发送报文给对方主机。
源端口和目的端口的字段(16位)是在 TCP 头部中,作用是告诉 TCP 协议应该把报文发给哪个进程。
1.6 有一个 IP 的服务器监听了一个端口,它的 TCP 的最大连接数是多少?
服务器通常固定在某个本地端口上监听,等待客户端的连接请求。
因此,客户端 IP 和 端口是可变的,其理论值计算公式如下:
对 IPv4,客户端的 IP 数最多为 2 的 32 次方,客户端的端口数最多为 2 的 16 次方,也就是服务端单机最
大 TCP 连接数,约为 2 的 48 次方。
1.7 UDP 和 TCP 有什么区别呢?分别的应用场景是?
UDP 不提供复杂的控制机制,利用 IP 提供面向「无连接」的通信服务
UDP 协议真的非常简,头部只有 8 个字节( 64 位),UDP 的头部格式如下:
目标和源端口:主要是告诉 UDP 协议应该把报文发给哪个进程。
包⻓度:该字段保存了 UDP 首部的⻓度跟数据的⻓度之和。
校验和:校验和是为了提供可靠的 UDP 首部和数据而设计。
1.8 说一下TCP 和 UDP 区别吧,越详细越好!
1.连接
TCP 是面向连接的传输层协议,传输数据前先要建立连接。
UDP 是不需要连接,即刻传输数据。
2.服务对象
TCP 是一对一的两点服务,即一条连接只有两个端点。
UDP 支持一对一、一对多、多对多的交互通信
3.可靠性
TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
UDP 是尽最大努力交付,不保证可靠交付数据。
4.拥塞控制、流量控制
TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。
5.首部开销
TCP 首部⻓度较⻓,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变⻓的。
UDP 首部只有 8 个字节,并且是固定不变的,开销较小。
6.传输方式
TCP 是流式传输,没有边界,但保证顺序和可靠。
UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。
7.分片不同
TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片。目标主机收到后,也同样在传输层组装 TCP
数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层,但是如果中途丢了一个分片,在实现可靠传输的 UDP 时则就需要重传所有的数据包,这样传输效率非常差,所以通常 UDP 的报文应该小于 MTU。8.
1.9 TCP 和 UDP 应用场景:
由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:
FTP 文件传输
HTTP / HTTPS
由于 UDP 面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于:
包总量较少的通信,如 DNS 、 SNMP(简单网络管理协) 等
视频、音频等多媒体通信
广播通信
1.10 为什么 UDP 头部没有「首部⻓度」字段,而 TCP 头部有「首部⻓度」字段呢?
原因是 TCP 有可变⻓的「选项」字段,而 UDP 头部⻓度则是固定的(8个字节),无需多一个字段去记录 UDP 的首部⻓度。
1.11 为什么 UDP 头部有「包⻓度」字段,而 TCP 头部则没有「包⻓度」字段呢?
先说说 TCP 是如何计算数据⻓度:
其中 IP 总⻓度 和 IP 首部⻓度,在 IP 首部格式是已知的。TCP 首部⻓度,则是在 TCP 首部格式已知的,所以就可以求得 TCP 数据的⻓度。
大家这时就奇怪了问:“ UDP 也是基于 IP 层的呀,那 UDP 的数据⻓度也可以通过这个公式计算呀? 为何还要有「包⻓度」呢?”
这么一问,确实感觉 UDP 「包⻓度」是冗余的。
因为为了网络设备硬件设计和处理方便,首部⻓度需要是 4 字节的整数倍。
如果去掉 UDP 「包⻓度」字段,那 UDP 首部⻓度就不是 4 字节的整数倍了,所以小林(本文的作者)觉得这可能是为了补全UDP 首部⻓度是 4 字节的整数倍,才补充了「包⻓度」字段。
二、TCP 连接建立
2.1 TCP 三次握手过程和状态变迁
TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手来进行的。
注:
client_isn和server_isn是两个随机码。
第三次握⼿是可以携带数据的,前两次握⼿是不可以携带数据的
关于三次握手更为详细的请见 小林的 图解HTTP
2.2 如何在 Linux 系统中查看 TCP 状态?
TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。
2.3 为什么是三次握手?不是两次、四次?(高频)
TCP 建立连接时,通过三次握手 能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号 。序列号能够保证数据包不重复、不丢弃和按序传输。
不使用「两次握手」和「四次握手」的原因:
「两次握手」:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数
具体细节请参考 小林的 图解HTTP
2.4 为什么客户端和服务端的初始序列号 ISN 是不相同的?
如果一个已经失效的连接被重用了,但是该旧连接的历史报文还残留在网络中,如果序列号相同,那么就无法分辨出该报文是不是历史报文,如果历史报文被新的连接接收了,则会产生数据错乱。
所以,每次建立连接前重新初始化一个序列号主要是为了通信双方能够根据序号将不属于本连接的报文段丢弃。
另一方面是为了安全性,防止黑客伪造的相同序列号的 TCP 报文被对方接收。
2.5 既然 IP 层会分片,为什么 TCP 层还需要 MSS 呢?
我们先来认识下 MTU 和 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 内核参数,控制队列大小和当队列满时应做什么处理
当网卡接收数据包的速度大于内核处理的速度时,会有一个队列保存这些数据包。控制该队列的最大值如下 参数:net.core.netdev_max_backlog
SYN_RCVD 状态连接的最大个数:net.ipv4.tcp_max_syn_backlog
超出处理能时,对新的 SYN 直接回报 RST,丢弃连接:net.ipv4.tcp_abort_on_overflow
避免 SYN攻击方式二
当 「 SYN 队列」满之后,后续服务器收到 SYN 包,不进入「 SYN 队列」;
计算出一个 cookie 值,再以 SYN + ACK 中的「序列号」返回客户端,
服务端接收到客户端的应答报文时,服务器会检查这个 ACK 包的合法性。如果合法,直接放入到「 Accept队列」。
最后应用通过调用 accpet() socket 接口,从「 Accept 队列」取出的连接。
三、TCP 连接断开
3.1 TCP 四次挥手过程和状态变迁
天下没有不散的宴席,对于 TCP 连接也是这样, TCP 断开连接是通过四次挥手方式。
双方都可以主动断开连接,断开连接后主机中的「资源」将被释放
可以看到,每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手。
需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。
3.2 为什么挥手需要四次
关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等
服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。
从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发
送,从而比三次握手导致多了一次。
3.3 为什么 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 时间将重新计时。
在 Linux 系统里 2MSL 默认是 60 秒,那么一个 MSL 也就是 30 秒。Linux 系统停留在 TIME_WAIT 的时
间为固定的 60 秒。
3.4 为什么需要 TIME_WAIT 状态?
主动发起关闭连接的一方,才会有 TIME-WAIT 状态。
需要 TIME-WAIT 状态,主要是两个原因:
防止具有相同「四元组」的「旧」数据包被收到;
保证「被动关闭连接」的一方能被正确的关闭,即保证最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭;
如果 TIME-WAIT 等待足够⻓的情况就会遇到两种情况:
服务端正常收到四次挥手的最后一个 ACK 报文,则服务端正常关闭连接。
服务端没有收到四次挥手的最后一个 ACK 报文时,则会重发 FIN 关闭连接报文并等待新的 ACK 报
文。
所以客户端在 TIME-WAIT 状态等待 2MSL 时间后,就可以保证双方的连接都可以正常的关闭。
3.5 TIME_WAIT 过多有什么危害?
如果服务器有处于 TIME-WAIT 状态的 TCP,则说明是由服务器方主动发起的断开请求。
过多的 TIME-WAIT 状态主要的危害有两种:
第一是内存资源占用;
第二是对端口资源的占用,一个 TCP 连接至少消耗一个本地端
第二个危害是会造成严重的后果的,要知道,端口资源也是有限的。如果发起连接一方的 TIME_WAIT 状态过多,占满了所有端口资源,则会导致无法创建新连接。
3.6 如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP 有一个机制是保活机制。这个机制的原理是这样的:
定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。
总结
OK,今天关于 计算机网络之 TCP 三次握手、四次挥手 面试题分享就到这里,希望本篇文章能够帮助到大家,同时也希望大家看后能学有所获!!!
好了,我们下期见~