个人博客


1、概念

  • 同步SYN:建立连接标志。当SYN=1,ACK=0时表示:这是一个连接请求报文段。若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0。
  • 终止FIN:关闭连接标志。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放运输连接。
  • 确认ACK:占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效。
  • 序列号seq:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号;序列号seq就是这个报文段中的第一个字节的数据编号。
  • 确认号ack:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号;序列号表示报文段携带数据的第一个字节的编号。

ACK、SYN和FIN这些大写的单词表示标志位,其值要么是1,要么是0;ack、seq小写的单词表示序号。

2、三次握手

  1. 客户端主动发送SYN=1,随机产生seq=x 的数据包到服务器(服务器由SYN=1知道客户端要求建立连接)。客户端进入SYN-SENT状态。
  2. 服务器收到请求后要确认联机信息,向客户端发送SYN=1,ACK=1,随机产生seq=y,ack=x+1。服务端进入SYN-RCVD状态。
  3. 客户端收到后检查ack是否正确,即第一次发送的seq+1,以及位码ACK是否为1。若正确客户端会再次发送 ACK=1,ack=y+1,seq=x+1(如果不携带数据则不消耗seq序列号),服务端收到后确认ack的值与ACK=1,则连接建立成功。

2.1、SYN攻击

client在发送第一次握手后就下线,导致server会不断重试发SYN+ACK包(Linux系统默认重试5次,每次时间翻倍,总共63秒才断开),最后服务器的SYN连接队列耗尽导致正常请求不能访问服务器。

2.2、Linux的防护办法

在SYN队列满后,会回发SYN Cookie参数给client,正常client则会回发SYN Cookie,直接建立连接。

3、四次挥手

  1. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
  2. 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据但不再发送数据给服务器)。
  3. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
  4. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。但是服务器只要接收到最后一次握手则立即进入CLOSED状态。

4、问题

4.1、为什么连接的时候是三次握手,关闭的时候却是四次握手?

建立连接时,服务器可以直接发送SYN+ACK报文。但是在关闭连接时,服务器不能直接关闭连接,需要等待数据发送完成再通知一次客户端关闭连接,所以会多一次挥手。
或者说关闭连接时,客户端和服务器都各需一次请求关闭(FIN)和确认关闭(ACK)。而建立连接时,服务器的那次握手既是对客户端第一次握手的响应也是对客户端建立连接的请求。

4.2、为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

防止客户端最后一次挥手确认服务器没有收到导致服务器不断重试发送FIN报文。等到2MSL后客户端会认为服务器已经关闭了连接这时候才可以放心地去关闭客户端的连接。

4.3、服务器出现大量CLOES_WAIT的原因?

代码不合理没有释放资源或者服务器连接池配置不合理。
通过命令行查看TCP连接各个状态的数量:
netstat -n|awk '/^tcp/{++S[$NF]}END{for(a in S) print a, S[a]}'

参考链接