TCP的三次握手四次挥手
seq序号:占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
ack确认号:占32位,只有ACK标志位为1时,确认序号字段才有效,ack=seq+1。
确认ACK标志位:仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1.
同步SYN标志位:在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中式SYN=1和ACK=1.因此,SYN置为1就表示这是一个连接请求或连接接受报文。
终止FIN标志位:用来释放一个连接。当FIN=1时,表示此报文段的发送方的数据已发送完毕,并要求释放运输连接。
三次握手
TCP/IP 协议是传输层的一个面向连接的安全可靠的一个传输协议,三次握手的机制是为了保证能建立一个安全可靠的连接,那么第一次握手是由客户端发起。
握手之前主动打开连接的客户端结束CLOSED阶段,被动打开的服务器端也结束CLOSED阶段,并进入LISTEN阶段。随后开始“三次握手”:
-
客户端会向服务端发送一个报文,在报文里面:
- SYN=1,表示请求建立新连接
- 同时选择一个初始序号seq=x
- TCP客户进程进入SYN_SENT(同步已发送)状态。
当服务端收到这个报文之后就知道客户端要和我建立一个新的连接。
-
于是服务端就向客户端发送一个确认消息包,在这个消息包里面:
- ACK=1,表示确认客户端发起的第一次连接请求
- 确认号是ack=x+1,表示收到客户端的序号seq并将其值加1作为自己确认号ack的值 。
- SYN=1,表示接受连接,同时也为自己选择一个初始序号seq=y。
- 这时TCP服务器进程进入SYN-RCVD(同步收到)状态。
-
第三次握手就是当客户端收到服务端发送的确认响应报文之后,还要继续去给服务端进行回应:
- 也需要一个ACK=1的确认标志位
- 确认号为ack=y+1,表示收到服务器序号seq,并将其值加1作为自己的确认号ack的值;
- 序号seq=x+1,表示收到服务器端的确认号ack,并将其值作为自己的序号值
- 随后客户端进入ESTABLISHED(已建立连接)阶段。
服务器收到来自客户端的“确认收到服务器数据”的TCP报文之后,明确了从服务器到客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。
通过以上三次连接,不管是客户端还是服务端,都知道我既能给对方发送消息,也能收到对方的响应。那么,这个连接就被安全的建立了。
为什么要进行第三次握手,而不是两次?
正如上文所描述的,为了实现可靠传输,发送方和接收方始终需要同步( SYNchronize )序号。 需要注意的是, 序号并不是从 0 开始的, 而是由发送方随机选择的初始序列号 ( Initial Sequence Number, ISN )开始 。 由于 TCP 是一个双向通信协议, 通信双方都有能力发送信息, 并接收响应。 因此, 通信双方都需要随机产生一个初始的序列号, 并且把这个起始值告诉对方。
于是, 这个过程就变成了下面这样。
注意:如果四次,那么就造成了浪费,因为在三次结束之后,就已经可以保证客户端可以给服务端发信息,客户端可以收到服务端的信息; 服务端可以给客户端发信息,服务端可以收到客户端的信息。
SYN攻击
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:
netstat -nap | grep SYN_RECV
四次挥手
四次挥手机制也是由客户端去发起。
-
客户端会向服务端发送一个报文,在报文里面:
- FIN位终止控制位置1,表示“请求释放连接“;
- 序号为seq=u,它等于客户端前面已传送过去的数据的最后一个字节的序号加1;
- 随后客户端进入FIN-WAIT-1(终止等待1)阶段,即半关闭阶段。
-
当服务端收到这个报文之后,我就知道了客户端想要和我断开连接,但是此时服务端不一定能做好准备,因为当客户端发起断开连接的这个消息的时候,对于服务端而言,他和还有可能有未发送完的消息,他还要继续发送,所以呢,此时对于服务端而言,我只能进行一个消息确认,就是我先告诉服务端,我知道你要给我断开连接了,但是我这里边还可能没有做好准备,你需要等我一下,等会儿我会告诉你,于是返回一段TCP报文,其中:
- 标记位为ACK,表示“接收到客户端发送的释放连接的请求”;
- 序号为seq=v,它等于服务端前面已传送过去的数据的最后一个字节的序号加1;
- 确认号为ack=u+1,表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值
- 随后服务器端开始准备释放服务器端到客户端方向上的连接。
前"两次挥手"既让服务器端知道了客户端想要释放连接,也让客户端知道了服务器端了解了自己想要释放连接的请求。于是,可以确认关闭客户端到服务器端方向上的连接了
-
服务器端自从发出ACK确认报文之后,经过CLOSED-WAIT阶段,做好了释放服务器端到客户端方向上的连接准备,再次向客户端发出一段TCP报文,其中:
- 标记位FIN=1,ACK=1,表示“已经准备好释放连接了”。注意:这里的ACK并不是确认收到服务器端报文的确认报文。
- 序号为seq=w(在半关闭状态服务端可能又发送了一些数据);
- 确认号为ack=u+1,重复上次已发送过的确认号;
-
客户端同样要给服务端继续发送一个消息确认的报文:
- 标记位为ACK=1,表示“接收到服务器准备好释放连接的信号”。
- 序号为seq=u+1;表示是在收到了服务器端报文的基础上,将其确认号ack值作为本段报文序号的值。
- 确认号为ack=w+1;表示是在收到了服务器端报文的基础上,将其序号seq值加1作为本段报文确认号的值。
- 随后客户端开始在TIME-WAIT阶段等待2MSL。
后“两次挥手”既让客户端知道了服务器端准备好释放连接了,也让服务器端知道了客户端了解了自己准备好释放连接了。于是,可以确认关闭服务器端到客户端方向上的连接了,由此完成“四次挥手”。
为什么是4次挥手?
TCP连接是双向传输的对等模式(即双方都可以同时向对方发送/接受数据),当有一方要关闭连接时,会发生FIN
告知对方,对方回一个ACK
则一个方向上的连接关闭了 ,所以需要两个FIN才能断开。
当服务端收到客户端发送过来的FIN
断开请求时,回复ACK后只是断开了客户端 -> 服务端
方向的连接,服务端还可以继续向客户端发送数据(若数据没有发送完)。数据发送完后,服务端也发送一个FIN,客户端回复ACK,则全部断开了
为什么客户端在TIME-WAIT阶段要等2MSL?
为的是确认服务器端是否收到客户端发出的ACK确认报文
当客户端发出最后的ACK确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完ACK确认报文之后,会设置一个时长为2MSL的计时器。MSL指的是Maximum Segment Lifetime:一段TCP报文在传输过程中的最大生命周期。2MSL即是服务器端发出为FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长。
服务器端在1MSL内没有收到客户端发出的ACK确认报文,就会再次向客户端发出FIN报文;