TCP三次握手概述。
为什么是三次握手,而不是两次?序列号?
什么是半连接,会导致什么问题?
三次握手的概述
三次握手过程:
1、双方一开始都处于CLOSED状态
2、svr主动监听某个端口,处于LISTEN状态
3、cli主动发送SYN包进行,发起连接后,处于SYN-SENT状态
4、svr收到cli发起的连接后,发送自己的SYN包,包中的seq是自己的序号;同时 携带的确认序号ACK = cli发来的seq+1,处于SYN-RCVD状态
5、cli收到svr发送的SYN+ACK包后,发送svr的ACK的ACK包,携带的序号都对应+1,处于ESTABLISHED状态
6、svr收到cli的ACK包后,处于ESTABLISHED状态
(如果不清楚TCP各个状态的含义,可以阅读我的笔记文章《TCP的11种状态》;SYN包、ACK包表示该TCP包头部的syn状态位=1或者ack状态位=1,syn=1表示建立连接,ack=1表示响应)
为什么是三次握手,而不是两次?序列号?
这里需要了解TCP中的序列号的相关内容,TCP的三次握手主要是为了沟通确认TCP包的序列号,为了实现可靠数据传输,TCP协议的通信双方,都必须维护一个序列号,以标识发送出去的数据包中,哪些是已经被对方接收到的。
在上面三次握手过程中步骤4里,cli收到的序列号ACK=x+1,表示svr期望cli下次发送的序列号是x+1,如果收到的不是x+1则不会做任何操作。那么相应的,svr也应该收到cli期望的序列号,如果只有两次握手,那么svr则无法知道cli的期望序列号,接下来也就无法继续通信。
需要补充的是,序列号的起始不是从1开始的,是随机产生的。如果序列号每次都是从1开始,那么我们考虑下面的一种情况,cli和svr第一次建立连接后,发送了序号为10的包,但是这个包在网络中迷路了,此时cli和svr断开了连接,又重新建立了新的连接,在这个新连接中,svr收到了序号9的包,正在等待序号10的包,此时上一次连接中的序号10的包又出现了,那么svr就会无法识别这个序号10的包是不是本次连接的包,从而导致问题。
什么是半连接,会导致什么问题?
在三次握手中,前两次握手都正常时,当svr在等待cli发起的第三次握手时,cli一直没有发送ACK,此时svr就会一直处理SYN-RCVD状态,会等待连接中,这就是半连接。
在svr进行了第二次握手后,会处于SYN-RCVD的状态,此时会将该socket存放在syn queue(半连接队列)中,如果收到了cli发来的第三次握手,那么会将socket从syn queue中取出,放到accept queue(全连接队列)。如果cli没有发送ACK,那么socket就会一直处理syn queue中,如果半连接数很多,就会导致svr消耗更多的内存资源,由此也就导致了半连接攻击。svr不停的分配内存资源,直到资源耗尽。
SYN flood攻击,是当前网络上最常见的DDoS攻击,也是最经典的拒绝服务攻击,它就是利用了TCP半连接的问题,通过向目标端口发送大量伪造源地址的攻击包文,造成目标服务器中半连接队列被占满,从而阻止其他合法用户进行访问。
解决方法:开启防火墙,限制单IP的SYN包的频率;对Linux内核参数进行优化:tcp_syncookies = 1、tcp_max_syn_backlog = 16384、somaxconn = 16384、tcp_synack_retries = 0。增大TCP半连接数队列的长度,降低tcp_synack_retries的值,使得半连接可以快速释放。