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
socket通信报文格式
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 状态,此后双⽅就可以开始通信了
CLOSED - 没有任何连接状态;每个状态的含义
3.2 TCP 链接的释放过程——四次挥⼿
建⽴⼀个连接需要三次握⼿,⽽终⽌⼀个连接要经过四次握⼿当服务端或者客户端不想再与对⽅进⾏通信之后,双⽅任意⼀⽅都可以主动发起断开链接的请求,我们还是以客户端主动发起为例客户端由于已经没有任何需要发送给服务端的消息了,所以发起断开客户端到服务端的通道请求
服务端收到该请求后同意了 ⾄此客户端到服务端的单项通道断开
服务端这个时候不会⽴刻朝客户端发器请求说那我也断开到你家的通道吧,服务端需要想想我⼿上还有没有需要发送给客户端的消息,如果还有的话,那我不能⽴马断开,先把数据发完才能断等服务端检查完毕之后发送也没有数据要给客户端了,这个时候就会朝客户端发起断开服务端到客户端的通道请求
客户端同意该请求,⾄此四次挥⼿完成
数据发送之后⽤户端发出分⼿请求,服务端会⽴马同意,但是服务端⾃⼰不会⽴即发出分⼿请求,因为可能还有数据传输没有完成,检测数据是否发送后才会提出分⼿,⼀次中间两次不能合并
1. 客户端进程数据发送完毕,停⽌发送数据,发出连接释放报⽂,释放标志位FIN=1,序列号seq=u(等于前⾯已经传送过来的数据的最后⼀个字节的序号加1),此时,客户端进⼊
FIN-WAIT-1(终⽌等待1)状态。 TCP规定,FIN报⽂段即使不携带数据,也要消耗⼀个序号。
2. 服务器收到连接释放报⽂,在确认⾃⼰受到所有数据后,发出确认报⽂同意断开,确认标志位ACK=1,确认号ack=u+1,并且带上⾃⼰的序列号seq=v,此时,服务端就进⼊了
CLOSE-WAIT(关闭等待)状态。TCP服务器通知⾼层的应⽤进程,客户端向服务器⽅向的⼀条通道马上就关闭了。这时候TCP处于半连接状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续⼀段时间,也就是整个CLOSE-WAIT状态持续的时间。
3. 客户端收到服务器的确认请求后,断开⾃⼰的⼀条通道。此时,客户端就进⼊FIN-WAIT-2(终⽌等待2)状态,在此期间还需要接收服务器传过来的数据,等待服务器发送连接释
放报⽂
4. 服务器的数据也发送完毕后,就向客户端发送连接释放报⽂,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能⼜发送了⼀些数据,假定此时的序列号为seq=w,此时,服务
器就进⼊了LAST-ACK(最后确认)状态,等待客户端的确认。
5. 客户端收到服务器的连接释放报⽂后,确认⾃⼰全部接收完数据后,必须发出确认,ACK=1,ack=w+1,⽽⾃⼰的序列号是seq=u+1,此时,客户端就进⼊了TIME-WAIT(时间
等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报⽂段寿命)的时间后,当客户端撤销相应的TCB后,才进⼊CLOSED状态。
6. 服务器只要收到了客户端发出的确认,⽴即进⼊CLOSED状态。可以看到,服务器结束TCP连接的时间要⽐客户端早⼀些
四、常见⾯试问题
【问题1】TCP为什么采⽤三次握⼿,若采⽤⼆次握⼿可以吗?
肯定不⾏,三次握⼿可以有效避免已经失效的连接请求报⽂突然⼜传送到了服务器,从⽽产⽣错误和资源浪费。
如果采⽤两次握⼿建⽴连接,假设有这样⼀种场景,客户端发送了第⼀个请求连接并且没有丢失,只是因为在⽹络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报⽂,以为服务器没有收到,此时重新向服务器发送这条报⽂,此后客户端和服务器经过两次握⼿完成连接,传输数据,然后关闭连接。此时此前滞留的那⼀次请求连接,⽹络通畅了到达了服务器,这个报⽂本该是失效的,但是,两次握⼿的机制将会让客户端和服务器再次建⽴连接,这将导致不必要的错误和资源的浪费。
如果采⽤的是三次握⼿,就算是那⼀次失效的报⽂传送到服务端,服务端接受到了那条失效报⽂并且回复了确认报⽂,但是客户端很清楚⾃⼰并没有发送额外请求,知道这是个作废的请求,因此不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接,
从另外⼀个⾓度讲,只有三次握⼿才能确认彼此的接受与发送能⼒是否正常,⽽两次却不可以
第⼀次:客户端发送请求到服务器,服务器知道客户端发送,⾃⼰接收正常
第⼆次:服务器发给客户端,客户端知道⾃⼰发送、接收正常,服务器接收、发送正常
第三次:客户端发给服务器:服务器知道客户端发送,接收正常,⾃⼰接收,发送也正常
【问题2】三次握⼿过程中可以携带数据吗?
第⼀次、第⼆次握⼿不可以携带数据,⽽第三次握⼿是可以携带数据的。
第⼀次不可以携带数据,也不可以先将数据缓存下来,等握⼿成功再提交给应⽤程序,这样会放⼤SYN FLOOD攻击。如果有⼈要恶意攻击服务器,那他每次都在第⼀次握⼿中的 SYN 报⽂中放⼊⼤量的数据,因为攻击者根本就不理服务器的接收、发送能⼒是否正常,然后疯狂着重复发 SYN 报⽂的话,这会让服务器花费很多时间、内存空间来接收这些报⽂。也就是说,第⼀次握⼿可以放数据的话,其中⼀个简单的原因就是会让服务器更加容易受到攻击了。
第三次的话,能够发出第三次握⼿报⽂的主机,肯定接收到第⼆次握⼿报⽂,因为伪造IP的主机是不会接收到第⼆次报⽂的。所以,能够发出第三次握⼿报⽂的,应该是合法的⽤户。尽管服务器侧的状态还没有“established”,接收到第三次握⼿的瞬间,状态就会切换为“established”,⾥⾯携带的数据按照正常流程⾛就好
【问题3】如果已经建⽴了连接,但是客户端突然出现故障了怎么办?
TCP还设有⼀个保活计时器SYN Timeout,显然,客户端如果出现故障,服务器不能⼀直等下去,⽩⽩
浪费资源。服务器每收到⼀次客户端的请求后都会重新复位这个计时器,时间通常是设置为2⼩时,若两⼩时还没有收到客户端的任何数据,服务器就会发送⼀个探测报⽂段,以后每隔75秒钟发送⼀次。若⼀连发送10个探测报⽂仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
【问题4】SYN洪⽔攻击原理
利⽤TCP协议缺陷,发送⼤量伪造的TCP连接请求,使被攻击⽅资源耗尽(CPU满负荷或内存不⾜)的攻击⽅式
具体原理是:TCP连接的三次握⼿中,假设⼀个⽤户向服务器发送了SYN报⽂后突然死机或掉线,那么服务器在发出SYN+ACK应答报⽂后是⽆法收到客户端的ACK报⽂的(第三次握⼿⽆法完成),这种情况下服务器端⼀般会重试(再次发送SYN+ACK给客户端)并等待⼀段时间后丢弃这个未完成的连接。这段时间的长度我们称为SYN Timeout,⼀般来说这个时间是分钟的数量级(⼤约为30秒~2分钟);⼀个⽤户出现异常导致服务器的⼀个线程等待1分钟并不是什么很⼤的问题,但如果有⼀个恶意的攻击者⼤量模拟这种情况(伪造IP地址),服务器端将为了维护⼀个⾮常⼤的半连接列表⽽消耗⾮常多的资源。即使是简单的保存并遍历也会消耗⾮常多的CPU时间和内存,何况还要不断对这个列表中的IP进⾏SYN+ACK的重试。实际上如果服务器的TCP/IP栈不够强⼤,最后的结果往往是堆栈溢出崩溃—— 即使服务器端的系统⾜够强⼤,服务器端也将忙于处理攻击者伪造的TCP连接请求⽽⽆暇理睬客户的正常请求(毕竟客户端的正常请求⽐率⾮常之⼩),此时从正常客户的⾓度看来,服务器失去响应
【问题5】为什么TIME_WAIT状态需要经过2MSL(Maximum Segment Lifetime)(最⼤报⽂段⽣存时间)才能返回到CLOSE状态?
虽然按道理,四个报⽂都发送完毕,我们可以直接进⼊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作为⼀个可靠传输协议,其可靠性就是依赖于收到对⽅的数据后的反馈机制,只有带着确认标志ACK给对⽅,这样对⽅才能确信数据已经被接收到了,才能⼤胆放⼼关闭通道释放缓存的数据
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, ….
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论