TCP三次握手和四次挥手面试题经典总结.docx
- 文档编号:4311982
- 上传时间:2022-11-29
- 格式:DOCX
- 页数:34
- 大小:2.48MB
TCP三次握手和四次挥手面试题经典总结.docx
《TCP三次握手和四次挥手面试题经典总结.docx》由会员分享,可在线阅读,更多相关《TCP三次握手和四次挥手面试题经典总结.docx(34页珍藏版)》请在冰豆网上搜索。
TCP三次握手和四次挥手面试题经典总结
1.TCP基本认识
1.TCP连接建立
1.TCP连接断开
1.Socket编程
PS:
本次文章不涉及TCP流量控制、拥塞控制、可靠性传输等方面知识,这些留在下篇哈!
正文
01TCP基本认识
瞧瞧TCP头格式
我们先来看看TCP头的格式,标注颜色的表示与本文关联比较大的字段,其他字段不做详细阐述。
TCP头格式
序列号:
在建立连接时由计算机生成的随机数作为其初始值,通过SYN包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。
用来解决网络包乱序问题。
确认应答号:
指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。
用来解决不丢包的问题。
控制位:
ACK:
该位为1时,「确认应答」的字段变为有效,TCP规定除了最初建立连接时的SYN包之外该位必须设置为1。
RST:
该位为1时,表示TCP连接中出现异常必须强制断开连接。
SYC:
该位为1时,表示希望建立连,并在其「序列号」的字段进行序列号初始值的设定。
FIN:
该位为1时,表示今后不会再有数据发送,希望断开连接。
当通信结束希望断开连接时,通信双方的主机之间就可以相互交换FIN位置为1的TCP段。
为什么需要TCP协议?
TCP工作在哪一层?
IP层是「不可靠」的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。
OSI参考模型与TCP/IP的关系
如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的TCP协议来负责。
因为TCP是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。
什么是TCP?
TCP是面向连接的、可靠的、基于字节流的传输层通信协议。
面向连接:
一定是「一对一」才能连接,不能像UDP协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
可靠的:
无论的网络链路中出现了怎样的链路变化,TCP都可以保证一个报文一定能够到达接收端;
字节流:
消息是「没有边界」的,所以无论我们消息有多大都可以进行传输。
并且消息是「有序的」,当「前一个」消息没有收到的时候,即使它先收到了后面的字节已经收到,那么也不能扔给应用层去处理,同时对「重复」的报文会自动丢弃。
什么是TCP连接?
我们来看看RFC793是如何定义「连接」的:
Connections:
ThereliabilityandflowcontrolmechanismsdescribedaboverequirethatTCPsinitializeandmaintaincertainstatusinformationforeachdatastream.
Thecombinationofthisinformation,includingsockets,sequencenumbers,andwindowsizes,iscalledaconnection.
简单来说就是,用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。
所以我们可以知道,建立一个TCP连接是需要客户端与服务器端达成上述三个信息的共识。
Socket:
由IP地址和端口号组成
序列号:
用来解决乱序问题等
窗口大小:
用来做流量控制
如何唯一确定一个TCP连接呢?
TCP四元组可以唯一的确定一个连接,四元组包括如下:
源地址
源端口
目的地址
目的端口
TCP四元组
源地址和目的地址的字段(32位)是在IP头部中,作用是通过IP协议发送报文给对方主机。
源端口和目的端口的字段(16位)是在TCP头部中,作用是告诉TCP协议应该把报文发给哪个进程。
有一个IP的服务器监听了一个端口,它的TCP的最大连接数是多少?
服务器通常固定在某个本地端口上监听,等待客户端的连接请求。
因此,客户端IP和端口是可变的,其理论值计算公式如下:
对IPv4,客户端的IP数最多为2的32次方,客户端的端口数最多为2的16次方,也就是服务端单机最大TCP连接数,约为2的48次方。
当然,服务端最大并发TCP连接数远不能达到理论上限。
首先主要是文件描述符限制,Socket都是文件,所以首先要通过ulimit配置文件描述符的数目;
另一个是内存限制,每个TCP连接都要占用一定内存,操作系统是有限的。
UDP和TCP有什么区别呢?
分别的应用场景是?
UDP不提供复杂的控制机制,利用IP提供面向「无连接」的通信服务。
UDP协议真的非常简,头部只有8个字节(64位),UDP的头部格式如下:
UDP头部格式
目标和源端口:
主要是告诉UDP协议应该把报文发给哪个进程。
包长度:
该字段保存了UDP首部的长度跟数据的长度之和。
校验和:
校验和是为了提供可靠的UDP首部和数据而设计。
TCP和UDP区别:
1.连接
TCP是面向连接的传输层协议,传输数据前先要建立连接。
UDP是不需要连接,即刻传输数据。
2.服务对象
TCP是一对一的两点服务,即一条连接只有两个端点。
UDP支持一对一、一对多、多对多的交互通信
3.可靠性
TCP是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
UDP是尽最大努力交付,不保证可靠交付数据。
4.拥塞控制、流量控制
TCP有拥塞控制和流量控制机制,保证数据传输的安全性。
UDP则没有,即使网络非常拥堵了,也不会影响UDP的发送速率。
5.首部开销
TCP首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是20个字节,如果使用了「选项」字段则会变长的。
UDP首部只有8个字节,并且是固定不变的,开销较小。
TCP和UDP应用场景:
由于TCP是面向连接,能保证数据的可靠性交付,因此经常用于:
FTP文件传输
HTTP/HTTPS
由于UDP面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于:
包总量较少的通信,如DNS、SNMP等
视频、音频等多媒体通信
广播通信
为什么UDP头部没有「首部长度」字段,而TCP头部有「首部长度」字段呢?
原因是TCP有可变长的「选项」字段,而UDP头部长度则是不会变化的,无需多一个字段去记录UDP的首部长度。
为什么UDP头部有「包长度」字段,而TCP头部则没有「包长度」字段呢?
先说说TCP是如何计算负载数据长度:
其中IP总长度和IP首部长度,在IP首部格式是已知的。
TCP首部长度,则是在TCP首部格式已知的,所以就可以求得TCP数据的长度。
大家这时就奇怪了问:
“UDP也是基于IP层的呀,那UDP的数据长度也可以通过这个公式计算呀?
为何还要有「包长度」呢?
”
这么一问,确实感觉UDP「包长度」是冗余的。
因为为了网络设备硬件设计和处理方便,首部长度需要是4字节的整数倍。
如果去掉UDP「包长度」字段,那UDP首部长度就不是4字节的整数倍了,所以小林觉得这可能是为了补全UDP首部长度是4字节的整数倍,才补充了「包长度」字段。
02TCP连接建立
TCP三次握手过程和状态变迁
TCP是面向连接的协议,所以使用TCP前必须先建立连接,而建立连接是通过三次握手而进行的。
TCP三次握手
一开始,客户端和服务端都处于CLOSED状态。
先是服务端主动监听某个端口,处于LISTEN状态
第一个报文——SYN报文
客户端会随机初始化序号(client_isn),将此序号置于TCP首部的「序号」字段中,同时把SYN标志位置为1,表示SYN报文。
接着把第一个SYN报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于SYN-SENT状态。
第二个报文——SYN+ACK报文
服务端收到客户端的SYN报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入TCP首部的「序号」字段中,其次把TCP首部的「确认应答号」字段填入client_isn+1,接着把SYN和ACK标志位置为1。
最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于SYN-RCVD状态。
第三个报文——ACK报文
客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文TCP首部ACK标志位置为1,其次「确认应答号」字段填入server_isn+1,最后把报文发送给服务端,这次报文可以携带客户到服务器的数据,之后客户端处于ESTABLISHED状态。
服务器收到客户端的应答报文后,也进入ESTABLISHED状态。
从上面的过程可以发现第三次握手是可以携带数据的,前两次握手是不可以携带数据的,这也是面试常问的题。
一旦完成三次握手,双方都处于ESTABLISHED状态,此致连接就已建立完成,客户端和服务端就可以相互发送数据了。
如何在Linux系统中查看TCP状态?
TCP的连接状态查看,在Linux可以通过netstat-napt命令查看。
TCP连接状态查看
为什么是三次握手?
不是两次、四次?
相信大家比较常回答的是:
“因为三次握手才能保证双方具有接收和发送的能力。
”
这回答是没问题,但这回答是片面的,并没有说出主要的原因。
在前面我们知道了什么是TCP连接:
用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。
所以,重要的是为什么三次握手才可以初始化Socket、序列号和窗口大小并建立TCP连接。
接下来以三个方面分析三次握手的原因:
三次握手才可以阻止历史重复连接的初始化(主要原因)
三次握手才可以同步双方的初始序列号
三次握手才可以避免资源浪费
原因一:
避免历史连接
我们来看看RFC793指出的TCP连接使用三次握手的首要原因:
Theprinciplereasonforthethree-wayhandshakeistopreventoldduplicateconnectioninitiationsfromcausingconfusion.
简单来说,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。
网络环境是错综复杂的,往往并不是如我们期望的一样,先发送的数据包,就先到达目标主机,反而它很骚,可能会由于网络拥堵等乱七八糟的原因,会使得旧的数据包,先到达目标主机,那么这种情况下TCP三次握手是如何避免的呢?
三次握手避免历史连接
客户端连续发送多次SYN建立连接的报文,在网络拥堵等情况下:
一个「旧SYN报文」比「最新的SYN」报文早到达了服务端;
那么此时服务端就会回一个SYN+ACK报文给客户端;
客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送RST报文给服务端,表示中止这一次连接。
如果是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可以在客户端(发送方)准备发送第三次报文时,客户端因有足够的上下文来判断当前连接是否是历史连接:
如果是历史连接(序列号过期或超时),则第三次握手发送的报文是RST报文,以此中止历史连接;
如果不是历史连接,则第三次发送的报文是ACK报文,通信双方就会成功建立连接;
所以,TCP使用三次握手建立连接的最主要原因是防止历史连接初始化了连接。
原因二:
同步双方初始序列号
TCP协议的通信双方,都必须维护一个「序列号」,序列号是可靠传输的一个关键因素,它的作用:
接收方可以去除重复的数据;
接收方可以根据数据包的序列号按序接收;
可以标识发送出去的数据包中,哪些是已经被对方收到的;
可见,序列号在TCP连接中占据着非常重要的作用,所以当客户端发送携带「初始序列号」的SYN报文的时候,需要服务端回一个ACK应答报文,表示客户端的SYN报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。
四次握手与三次握手
四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了「三次握手」。
而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。
原因三:
避免资源浪费
如果只有「两次握手」,当客户端的SYN请求连接在网络中阻塞,客户端没有接收到ACK报文,就会重新发送SYN,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的ACK确认信号,所以每收到一个SYN就只能先主动建立一个连接,这会造成什么情况呢?
如果客户端的SYN阻塞了,重复发送多次SYN报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
两次握手会造成资源浪费
即两次握手会造成消息滞留情况下,服务器重复接受无用的连接请求SYN报文,而造成重复分配资源。
小结
TCP建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。
序列号能够保证数据包不重复、不丢弃和按序传输。
不使用「两次握手」和「四次握手」的原因:
「两次握手」:
无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
「四次握手」:
三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。
为什么客户端和服务端的初始序列号ISN是不相同的?
因为网络中的报文会延迟、会复制重发、也有可能丢失,这样会造成的不同连接之间产生互相影响,所以为了避免互相影响,客户端和服务端的初始序列号是随机且不同的。
初始序列号ISN是如何随机产生的?
起始ISN是基于时钟的,每4毫秒+1,转一圈要4.55个小时。
RFC1948中提出了一个较好的初始化序列号ISN随机生成算法。
∙
ISN=M+F(localhost,localport,remotehost,remoteport)
M是一个计时器,这个计时器每隔4毫秒加1。
F是一个Hash算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值。
要保证Hash算法不能被外部轻易推算得出,用MD5算法是一个比较好的选择。
既然IP层会分片,为什么TCP层还需要MSS呢?
我们先来认识下MTU和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分片了。
握手阶段协商MSS
经过TCP层分片后,如果一个TCP分片丢失后,进行重发时也是以MSS为单位,而不用重传所有的分片,大大增加了重传的效率。
什么是SYN攻击?
如何避免SYN攻击?
SYN攻击
我们都知道TCP连接建立是需要三次握手,假设攻击者短时间伪造不同IP地址的SYN报文,服务端每接收到一个SYN报文,就进入SYN_RCVD状态,但服务端发送出去的ACK+SYN报文,无法得到未知IP主机的ACK应答,久而久之就会占满服务端的SYN接收队列(未连接队列),使得服务器不能为正常用户服务。
SYN攻击
避免SYN攻击方式一
其中一种解决方式是通过修改Linux内核参数,控制队列大小和当队列满时应做什么处理。
当网卡接收数据包的速度大于内核处理的速度时,会有一个队列保存这些数据包。
控制该队列的最大值如下参数:
∙
dev_max_backlog
SYN_RCVD状态连接的最大个数:
∙
net.ipv4.tcp_max_syn_backlog
超出处理能时,对新的SYN直接回RST,丢弃连接:
∙
net.ipv4.tcp_abort_on_overflow
避免SYN攻击方式二
我们先来看下Linux内核的SYN(未完成连接建立)队列与Accpet(已完成连接建立)队列是如何工作的?
正常流程
正常流程:
当服务端接收到客户端的SYN报文时,会将其加入到内核的「SYN队列」;
接着发送SYN+ACK给客户端,等待客户端回应ACK报文;
服务端接收到ACK报文后,从「SYN队列」移除放入到「Accept队列」;
应用通过调用accpet()socket接口,从「Accept队列」取出的连接。
应用程序过慢
应用程序过慢:
如果应用程序过慢时,就会导致「Accept队列」被占满。
受到SYN攻击
受到SYN攻击:
如果不断受到SYN攻击,就会导致「SYN队列」被占满。
tcp_syncookies的方式可以应对SYN攻击的方法:
∙
net.ipv4.tcp_syncookies = 1
tcp_syncookies应对SYN攻击
当「SYN队列」满之后,后续服务器收到SYN包,不进入「SYN队列」;
计算出一个cookie值,再以SYN+ACK中的「序列号」返回客户端,
服务端接收到客户端的应答报文时,服务器会检查这个ACK包的合法性。
如果合法,直接放入到「Accept队列」。
最后应用通过调用accpet()socket接口,从「Accept队列」取出的连接。
03TCP连接断开
TCP四次挥手过程和状态变迁
天下没有不散的宴席,对于TCP连接也是这样,TCP断开连接是通过四次挥手方式。
双方都可以主动断开连接,断开连接后主机中的「资源」将被释放。
客户端主动关闭连接——TCP四次挥手
客户端打算关闭连接,此时会发送一个TCP首部FIN标志位被置为1的报文,也即FIN报文,之后客户端进入FIN_WAIT_1状态。
服务端收到该报文后,就向客户端发送ACK应答报文,接着服务端进入CLOSED_WAIT状态。
客户端收到服务端的ACK应答报文后,之后进入FIN_WAIT_2状态。
等待服务端处理完数据后,也向客户端发送FIN报文,之后服务端进入LAST_ACK状态。
客户端收到服务端的FIN报文后,回一个ACK应答报文,之后进入TIME_WAIT状态
服务器收到了ACK应答报文后,就进入了CLOSE状态,至此服务端已经完成连接的关闭。
客户端在经过2MSL一段时间后,自动进入CLOSE状态,至此客户端也完成连接的关闭。
你可以看到,每个方向都需要一个FIN和一个ACK,因此通常被称为四次挥手。
这里一点需要注意是:
主动关闭连接的,才有TIME_WAIT状态。
为什么挥手需要四次?
再来回顾下四次挥手双方发FIN包的过程,就能理解为什么需要四次了。
关闭连接时,客户端向服务端发送FIN时,仅仅表示客户端不再发送数据了但是还能接收数据。
服务器收到客户端的FIN报文时,先回一个ACK应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN报文给客户端来表示同意现在关闭连接。
从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的ACK和FIN一般都会分开发送,从而比三次握手导致多了一次。
为什么TIME_WAIT等待的时间是2MSL?
MSL是MaximumSegmentLifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
因为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秒。
其定义在Linux内核代码里的名称为TCP_TIMEWAIT_LEN:
∙
∙
#defineTCP_TIMEWAIT_LEN(60*HZ)/*howlongtowaittodestroyTIME-WAIT
state,about60seconds*/
如果要修
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- TCP 三次 握手 四次 挥手 试题 经典 总结
![提示](https://static.bdocx.com/images/bang_tan.gif)