TCP三握(简述)
-
一开始,客户端和服务端都处于closed状态,服务端主动监听某个端口,处于listen状态
-
一握 要进行C-S的第一个SYN发送 ,客户端会随机初始化序列号(client_isn) 并将其置于TCP首部的序列号字段 中,并且将SYN标志位设置为1 ,表示这是一条SYN报文,将报文发送给服务端(报文中不包含应用层数据 ),客户端处于SYN-SENT状态
-
二握 要进行S-C的SYN+ACK 发送(SYN和ACK集成在一个报文中 )。此时服务端已经收到了客户端的报文,知道了client_isn。他会生成一个随机序列号(server_client ),放到响应报文的序列号中 ,然后把(client_isn+1)放到确认应答号字段中 ,将SYN和ACK字段设置为1 .(该报文也不包含应用层数据),服务端处于SYN-RCVD状态
-
三握要进行C-S的第二个SYN发送 客户端把收到的(server-isn+1 )放到确认应答号字段中 ,(本次报文中可以包含应用层数据 ),客户端处于ESTABLISHED状态
-
服务端 收到三握信息,进入ESTABLISHED状态
初始序列号ISN是如何随机产生的
ISN = M + F(localhost,localport,remotehost,remoteport)
M是一个计时器,这个计时器每隔四微秒加以1一
F是一个Hash算法,根据源IP,源端口,目的IP,目的端口生成一个随机数值
三次握手丢失分别发生什么
一握丢失
客户端发送的第一个SYN如果长时间没有收到响应会触发超时重传 ,并且重新传输的SYN报文的序列号不会发生改变
第一次超时重传一般第一次在1秒后,第二次在2秒后,三次4秒后,4次8秒后,每次超时重传的时间是上一次的两倍。
一般来说还会有一个超时重传次数上限,达到这个上限连接就会断开
二握丢失
二次握手内容分为两部分,对客户端的ACK和服务端自己的SYN
对第一次握手的ACK丢失 ,客户端一直收不到第一次握手的响应,可能触发客户端的超时重传机制
对于服务端自己的SYN丢失 ,服务端在完成握手后会等待第三次握手客户端发来的ACK报文,如果一直等不到,可能触发服务端的超时重传机制
不论是谁触发超时重传,只要到达他的最大超时重传次数,就会断开连接
三握丢失
三握是客户端给服务端发送SYN,服务端收不到SYN,就会触发超时重传机制,达到服务端的超时重传次数上限就服务端就会断开连接。
ACK报文不会有重传的,当ACK丢失了,就要由对方重传对应的报文
SYN攻击
什么是SYN攻击
在TCP三次握手时Linux内核会维护两个队列
-
半连接队列,也称SYN队列
-
全连接队列,也称accept队列
黑客会在短时间内伪造多个IP发送SYN请求给服务端,服务端会一一恢复ACK-SYN报文,进入SYN_RCVD状态,但是一直收不到客户端的ACK应答,久而久之,服务端的半连接队列就会被占满,使得服务端不能正常服务用户
如何避免SYN攻击
-
增大TCP半连接队列
-
使用syncookie功能,当半连接队列满了后,后续的SYN包不会丢弃,而是计算出一个cookie的值,夹在ACK的序列号中返回给客户端,收到客户端的ACK后验证cookie,放进Accept队列,再从Accept队列中取出连接使用
-
减少SYN-ACK重传次数,加速处于SYN_REVC状态的TCP连接断开,减少TCP半连接队列的压力
TCP四次挥手
开始处于正常连接状态,客户端和服务端都处于ESTABLISHED状态
-
此时,客户端想要断开连接,向服务端发送一个TCP首部FIN标志位被标记为1 的报文,即FIN报文 ,客户端进入FIN_WAIT_1状态
-
服务端收到报文后,向客户端发送ACK报文 ,服务端进入CLOSED_WAIT状态
-
客户端收到服务端的ACK报文后,进入FIN_WAIT_2状态
-
服务端接着处理未处理完的数据,处理完后也向客户端发送FIN报文 ,服务端进入LAST_ACK阶段
-
客户端收到FIN报文后,回应一个ACK报文,进入TIME_WAIT状态
-
服务端收到回应,进入CLOSED状态
-
客户端等待2MSL后,自动进入CLOSED状态
为什么要挥手四次
可以看到,在回复了客户端的FIN之后服务端可能仍然有要处理的数据和没来得及给客户端发送的响应,所以,要等服务端处理完手头的事务之后再给客户端发送FIN报文来同意断开连接,所以需要四次挥手
四次挥手丢失分别发生什么
第一次挥手
第一次挥手主要是客户端向服务端发送FIN报文,客户端已经进入FIN_WAIT_1状态了,如果一直收不到ACK,就触发超时重传机制,如果超过最大重传次数,就断开连接
第二次挥手
第二次挥手主要是服务端给客户端发送ACK,此时服务端进入CLOSE_WAIT状态,如果ACK丢失,ACK不会重传,客户端出发超时重传,重新发送FIN报文,超过次数上限连接断开
第三次挥手
处于CLOSE_WAIT状态的服务端会给客户端发送FIN报文,进入LAST_ACK状态,如果FIN丢失,一直收不到响应,服务端就进入超时重传,客户端再FIN_WAIT_2状态会有一个60秒的时间上限,等待超过60秒客户端就会主动断开连接
第四次挥手
客户端收到FIN报文,回复ACK报文,进入TIME_WAIT状态,这个状态会持续2MSL,如果服务端没有收到ACK,就会重传FIN,如果客户端在2MSL时间内收到了新的FIN,就会刷新等待时间,当然,超过服务端的超时重传次数限制之后服务端就会主动断开连接
TIME_WAIT状态
为什么TIME_WAIT等待时间是2MSL
MSL是报文最大生存时间,他是任何报文在网络上存在的最长时间,超过这个时间就会被丢弃。
网络中某些数据包可能来自发送方,这些发送方的数据包被接收方处理后又会向对方发送响应,一来一回需要等待两倍的时间
所以2MSL就是允许一次请求丢失
为什么需要TIME_WAIT状态
主动发起关闭连接的一方才会有TIME_WAIT状态
主要有两个原因
-
防止历史连接中的数据被后面相同四元组的链接错误的接收
-
保证被动关闭连接的一方能正确地关闭
TIME_WAIT过多有什么危害
-
占用系统资源
-
占用端口资源
如何优化TIME_WAIT
-
将长时间处于TIME_WAIT状态的socket为新的连接所用
-
限制系统中处于TIME_WAIT状态的连接数量
服务器出现大量TIME_WAIT状态的原因
-
HTTP没有使用长连接,这种情况一般都是由服务端主动断开连接的,所以服务端会存在处于TIME_WAIT状态的连接
-
HTTP长连接超时,此时服务端主动断开连接,就会产生处于TIME_WAIT状态的连接
-
HTTP长连接的请求数量达到上限,一般服务端都会设置长连接数量上限,长连接超过上限服务端就会主动关闭连接,产生TIME_WAIT状态的连接
连接建立后客户出问题怎么办
在连接建立后,服务端处于ESTABLISHED状态,如果客户端出问题,一直不关闭这个连接,就会占用资源,TCP有保活机制,会在连接建立后每隔一个时间段发送一个探测报文(报文中数据很少),如果连续几个报文都没有得到回应则认为该TCP连接已经死亡,会关闭该链接。
连接建立后服务端出问题怎么办
TCP的链接信息是内核维护的,当服务端出问题后,内核会介入,给客户端发送FIN报文,开始四次挥手,断开连接
TCP重传、滑动窗口、流量控制、拥塞控制