三次握手过程
1.客户端与客户端都处于close状态,服务器主动对某端口进行监听后处于LISTEN状态
2.客户端将SYN标志位置为1,向服务器发,SYN和初始序列号seq后处于SYN_SENT状态
2.服务器处于LISTEN状态,收到客户端发来的请求后将SYN和ACK的标志位置为1,将确认应答号ack填入客户端的seq+1;
向客户端发送SYN,服务器的初始序列号,ACK和确认应答序号为客户端初始序列号+1,服务器变为SYN_REVD状态
3.客户端收到服务器后,将ACK标志位置为1,确认应答号ack填入服务器的初始序列号+1;
发送ACK和确认应答号为服务器初始序列号+1,客户端变为ESTABLISHED状态,本次握手可以携带数据
4.服务器收到客户端的确认应答后,变为ESTABLISHED状态
为什么是3次握手而不是2次?
因为TCP协议是全双工通信,就是两端都可以发送数据;
三次握手的目的是确认自己和对方的发送接收功能都是正常的
而如果只有两次握手,
首先:
因为TCP协议是全双工通信,就是两端都可以发送数据;而此时客户端能知道服务器可以接受自己发送的数据,而服务器不知道客户端能否收到自己发送的数据;这就违背了全双工通信的定义;
其次:
因为网络往往是非理想状态,客户端第一次发送给服务器的连接请求,有时由于网络拥塞,迟迟没有得到响应;触发超时重传而后第二次发送请求,服务器回应,连接建立;一段时间后,第一次的连接请求到达服务器,服务器会创建一个冗余的连接,这样就造成了资源浪费;
总结:防止已失效的报文突然传送到服务器引起错误
第一次握手丢失会发生什么?
客户端发送SYN报文请求建立连接后,进入SYN_SENT状态,此时他会维持一个计时器,在一定时间内收不到服务器的SYN_ACK报文,就会触发超时重传机制,重传SYN报文,而且每次重传的SYN报文的序列号都是一样的;每次重传时间是上一次的二倍,如果重传次数达到上限就会断开连接
注:这个超时时间是写在内核中的,如果想要修改需要重新编译内核,比较麻烦
第二次握手丢失会发生什么
1.因为第二次握手包含对客户端第一次握手的ACK确认报文,所以如果客户端在一定时间内没有收到二次握手报文,客户端会认为是自己的SYN报文丢失,会触发超时重传机制,重传SYN报文
2.因为第二次握手中报文服务器的SYN报文,客户端收到后需要发送ACK确认报文,这样服务器才会知道SYN报文成功被客户端接收;所以当二次握手丢失时,服务器也收不到第三次握手,服务器会触发超时重传机制重新发送SYN_ACK报文
3.当客户端与服务器到达最大重传次数时,就会断开连接
第三次握手丢失会发生什么
因为第三次握手的ACK是对第二次握手的SYN的确认报文,所以当第三次握手丢失,服务端在一定时间内收不到确认报文,就会触发超时重传机制,重传SYN_ACK报文;
注:第三次握手的ACK是不会重传的,若ACK丢失,就由对方重传对应报文
什么是SYN攻击
正常情况下:
1.当服务器接收到客户端的SYN报文时,会创建一个半连接对象,然后将其加入到内核的【SYN队列】
2.接着发送SYN+ACK给客户端,等待客户端回应ACK报文;
3.服务器接收到ACK报文后,从【SYN队列】中取出一个半连接对象,然后创建一个新的连接对象放入【accept队列】
4.应用通过调用accept() socket接口,从【accept队列】中取出连接对象
无论是【SYN队列】还是【accept队列】,都有最大长度限制,超过限制时,默认情况都会丢弃报文
所以,SYN攻击方式就是短时间伪造不同IP地址的SYN报文发送给服务端,久而久之就会占满TCP半连接队列,这样当TCP半连接队列满了,后续就算再收到正确的SYN报文也会丢弃,导致客户端无法和服务器建立连接
什么是半/全连接队列
半连接队列(SYN队列):服务器第一次收到客户端的SYN后,会处于SYN_RCVD状态,此时双方还没有完全建立连接,服务器会把此状态下的请求连接放在一个队列里,我们把这种队列成为半连接队列
全连接队列(accept队列):已经完成三次握手,建立起连接的就会放在全连接队列