参考资料:
[1].【TCP】详解TCP 三次握手和四次挥手
[2]. iOS:为什么TCP连接要三次握手,四次挥手
[3]. 面试官,不要再问我三次握手和四次挥手
- 什么是三次握手?
三次握手(Three-way Handshake)其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。
三次握手交换的信息
除了交换彼此的初始序列号,三次握手的另一个重要作用是交换一些辅助信息,比如最大段大小(MSS)、窗口大小(Win)、窗口缩放因子(WS)、是否支持选择确认(SACK_PERM)
- 为什么需要三次握手,两次不行吗?
服务器并不能确认客户端的接收能力是否正常。
弄清这个问题,我们需要先弄明白三次握手的目的是什么,能不能只用两次握手来达到同样的目的。
第一次握手:客户端发送网络包,服务端收到了。
这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。
这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。
这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
因此,需要三次握手才能确认双方的接收与发送能力是否正常。
下面这个例子还说明了,服务器收到了连接请求需要再确认才可以。
如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源。
- SYN攻击是什么?
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。
检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击。
常见的防御 SYN 攻击的方法有如下几种:
缩短超时(SYN Timeout)时间
增加最大半连接数
过滤网关防护
SYN cookies技术
- 为什么断开连接需要四次挥手呢,像建立连接的时候一样,三次行不行呢?
参考[2],四次挥手的原因主要是就是一个单方要断开连接,表示不会发送数据,但是另外一方可能还要继续发送数据,发完才断开连接,所以不能那么快。
TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工 模式,这就意味着,在客户端想要断开连接时,客户端向服务端发送FIN报文,只是表示客户端已经没有数据要发送了,但是这个时候客户端还是可以接收来自服务端的数据。
当服务端接收到FIN报文,并返回ACK报文,表示服务端已经知道了客户端要断开连接,客户端已经没有数据要发送了,但是这个时候服务端可能依然有数据要传输给客户端。
当服务端的数据传输完之后,服务端会发送FIN报文给客户端,表示服务端也没有数据要传输了,服务端同意关闭连接,之后,客户端收到FIN报文,立即发送给客户端一个ACK报文,确定关闭连接。在之后,客户端和服务端彼此就愉快的断开了这次的TCP连接。
或许会有疑问,为什么服务端的ACK报文和FIN报文都是分开发送的,但是在三次握手的时候却是ACK报文和SYN报文是一起发送的,因为在三次握手的过程中,当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是在关闭连接时,当服务端接收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,你发的FIN报文我收到了,只有等到服务端所有的数据都发送完了,才能发送FIN报文,因此ACK报文和FIN报文不能一起发送。所以断开连接的时候才需要四次挥手来完成。
- 为什么断开时发送ACK的要等待4分钟(time_wait)最后再关闭链接?
这个时间是在接收到对方的fin开始的,是为了防止对方没有收到ack,如果对方没有收到ack,对方会重发fin,然后自己再重发ack。