TCP的三次握手四次挥手理解及面试题
一、TCP概述
每一条TCP连接都有两个端点,这种端点我们叫作套接字(socket),它的定义为端口号拼接到IP地址即构成了套接字,
例如,若IP地址为192.0.0.1 而端口号为8000,那么得到的套接字为192.0.0.1:8000
二、TCP报文格式
ACK、SYN和FIN这些大写的单词表示标志位,其值要么是1,要么是0;ack、seq小写的单词表示序号
同步SYN:(Synchronize ),SYN=1表示这是一个连接请求报文,或连接接受报文。SYN这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0
确认ACK:仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效。如:当SYN=1,ACK=0时表示这是一个连接请求报文段,若同意连接,则在响应报文段中使得SYN=1,ACK=1
终止FIN:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放
序列号seq:(Sequence Number),占4个字节,表示报文段携带数据的第一个字节的编号,TCP连接中传送的字节流中的每个字节都按顺序编号。例如,一段报文的序号值是 301 ,而携带的数据共有100字段,显然下一个报文段(如果还有的话)的数据序号应该从401开始;,图中的 x 和 y,
确认号ack:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号,例如,B收到了A发送过来的报文段,其序列号seq是1,而数据长度是100字节,这表明B正确的收到了A发送的到序号从1到100为止的数据。因此,B期望收到A的下一个数据序号是100+1,于是B在发送给A的确认报文段中把确认号置为101
三、三次握手,四次挥手
3.1 TCP连接的建立过程——三次握手
建立双向通道的过程称之为三次握手,建立通道的发起者可以是客户端也可以是服务端,下面我们就以客户端先主动发起为例
-
客户端会朝服务端发送一个请求询问服务端:"我能不能挖一条通往你家的地道"
-
服务端收到请求,回复说:"好吧 你挖吧",由于TCP是双向通道,客户端挖向服务端的通道只能给客户端朝服务端发消息使用,服务端要向给客户端发消息是没办法走这一条通道的,需要自己挖一条通往客户端的通道
所以服务端在回复同意客户端挖通道的同时还会问一句:"那我能不能也挖一条通往你家的通道"
-
客户端收到服务端请求后客户端到服务端的通道就挖成功了,然后也会同意服务端的请求,服务端挖向客户端的通道也会成功
- 1.服务器准备:TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态
- 2.客户端准备:TCP客户进程也是先创建传输控制块TCB
-
3.第一次握手:客户端向服务器发出连接请求报文,报文首部中的同步标志SYN=1,同时生成一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号
-
4.第二次握手:TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中确认标志 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号
-
5.第三次握手:TCP客户端收到确认后,还要向服务器再次给出确认。确认报文的确认标志ACK=1,确认号ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号
- 6.连接成功:当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了
LISTEN - 侦听来自远方TCP端口的连接请求; SYN-SENT -在发送连接请求后等待匹配的连接请求; SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认; ESTABLISHED- 代表一个打开的连接,数据可以传送给用户; FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认; FIN-WAIT-2 - 从远程TCP等待连接中断请求; CLOSE-WAIT - 等待从本地用户发来的连接中断请求; CLOSING -等待远程TCP对连接中断的确认; LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认; TIME-WAIT -等待足够的时间以确保远程TCP接收到连接中断请求的确认; CLOSED - 没有任何连接状态;
3.2 TCP链接的释放过程——四次挥手
建立一个连接需要三次握手,而终止一个连接要经过四次握手
当服务端或者客户端不想再与对方进行通信之后,双方任意一方都可以主动发起断开链接的请求,我们还是以客户端主动发起为例
-
客户端由于已经没有任何需要发送给服务端的消息了,所以发起断开客户端到服务端的通道请求
-
服务端收到该请求后同意了 至此客户端到服务端的单项通道断开
-
服务端这个时候不会立刻朝客户端发器请求说那我也断开到你家的通道吧,服务端需要想想我手上还有没有需要发送给客户端的消息,如果还有的话,那我不能立马断开,先把数据发完才能断
等服务端检查完毕之后发送也没有数据要给客户端了,这个时候就会朝客户端发起断开服务端到客户端的通道请求
-
客户端同意该请求,至此四次挥手完成
数据发送之后用户端发出分手请求,服务端会立马同意,但是服务端自己不会立即发出分手请求,因为可能还有数据传输没有完成,检测数据是否发送后才会提出分手,一次中间两次不能合并
四、常见面试问题
【问题1】
肯定不行,三次握手可以有效避免已经失效的连接请求报文突然又传送到了服务器,从而产生错误和资源浪费。
如果采用两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。
如果采用的是三次握手,就算是那一次失效的报文传送到服务端,服务端接受到了那条失效报文并且回复了确认报文,但是客户端很清楚自己并没有发送额外请求,知道这是个作废的请求,因此不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接,
从另外一个角度讲,只有三次握手才能确认彼此的接受与发送能力是否正常,而两次却不可以
-
-
第二次:服务器发给客户端,客户端知道自己发送、接收正常,服务器接收、发送正常
-
第三次:客户端发给服务器:服务器知道客户端发送,接收正常,自己接收,发送也正常
【问题2】三次握手过程中可以携带数据吗?
第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。
第一次不可以携带数据,也不可以先将数据缓存下来,等握手成功再提交给应用程序,这样会放大SYN FLOOD攻击。如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。也就是说,第一次握手可以放数据的话,其中一个简单的原因就是会让服务器更加容易受到攻击了。
第三次的话,能够发出第三次握手报文的主机,肯定接收到第二次握手报文,因为伪造IP的主机是不会接收到第二次报文的。所以,能够发出第三次握手报文的,应该是合法的用户。尽管服务器侧的状态还没有“established”,接收到第三次握手的瞬间,状态就会切换为“established”,里面携带的数据按照正常流程走就好
【问题3】如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器SYN Timeout,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
【问题4】SYN洪水攻击原理
利用TCP协议缺陷,发送大量伪造的TCP连接请求,使被攻击方资源耗尽(CPU满负荷或内存不足)的攻击方式
虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是网络是不可靠的,有可能最后一个ACK丢失。
在Client发送出最后的ACK回复后,该ACK可能丢失。所以TIME_WAIT状态就是用来确认对方是否收到ACK,服务器如果没有收到ACK,不知道客户端是否接收完全部数据不敢关闭自己的通道,将不断重复第三次挥手发送FIN片段。所以客户端不能立即关闭,它必须确认Server接收到了该ACK。
Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接
【问题6】RFC793明确规定,除了第一个握手报文SYN除外,其它所有报文必须将ACK = 1,RFC规定的背后肯定有合理性的一面,能否深究一下原因?
TCP报文是在IP网络上传输,丢包是家常便饭,接收方要抓住一切的机会,把消息告诉发送方。最方便的方式就是,任何我方发送的TCP报文,都要捎带着ACK状态位。
ACK状态位单独能承担这个消息传递的任务吗?
不能!需要有确认号ack配合才行。
如客户端序列号seq=x,则服务端接收完全部数据后应该告诉对面ack=x+1,客户端就知道服务端已经全部接收到
如果我方发出的ack= 10001,那意味着序列号10000及之前的字节已经成功接收
【问题7】(ISN)seq是固定的吗
三次握手的一个重要功能是客户端和服务端交换ISN(Initial Sequence Number), 以便让对方知道接下来接收数据的时候如何按序列号组装数据。
如果ISN是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。
【问题8】关于SYN-ACK 重传次数的问题
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超 过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s, 2s, 4s, 8s, ….