TCP字段详解与三次握手四次挥手过程
TCP数据包
术语 | 含义 |
---|---|
序号seq | 占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号 第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号; 序号seq就是这个报文段中的第一个字节的数据编号 |
确认号ack | 占4个字节,表示期待收到对方下一个报文段的第一个数据字节的序号; 确认号ack指的是期望接收到下一个字节的编号 当前报文段最后一个字节的编号+1即为将发送的确认号 |
数据偏移 | 由于头部有可选字段,长度不固定,因此数据偏移指出TCP报文数据距离TCP报文起始处有多远 |
URG | 紧急指针标志,为1时,紧急指针有效。表示此报文段中有紧急数据要被优先处理 |
ACK | 确认序号标志,为1时,确认号有效 |
PSH | push标志,为1时,接收端应用程序应该从TCP缓冲区把数据读走,不用等到缓存填满 |
RST | 重置连接标志,为1时,说明当前tcp出现严重错误,对方要求重新建立连接 |
SYN | 同步序列号,为1时,请求建立连接,并在其序列号的字段进行序列号的初始值设定。 |
FIN | finish标志,为1时,要求释放连接 |
滑动窗口(窗口) | 告知发送端,接收端的缓存大小,以此控制发送端发送的速率,从而流量控制 |
校验和 | 对整个TCP报文段(TCP头部、TCP数据)以16位计算所得,由发送端计算和存储,接收端验证 |
紧急指针 | URG为1时有效,指出本报文段中紧急数据的字节数 |
可选项 | 定义一些可选参数 MSS:每个TCP报文段中数据字段的最大长度 Windows scaling:窗口扩大选项 更多参考 https://wenku.baidu.com/view/5c288b51ad02de80d4d84066.html |
三次握手
目的:为了确认连接
1.第一次握手
C => [syn] => S
SYN=1,seq=x
客户端发送SYN包到服务器
客户端进入SYN_SEND状态
2.第二次握手
C <= [syn/ack] <= S
SYN=1,ACK=1,seq=y,ack=x+1
服务端确认客户端的SYN,发送ACK确认包+SYN包
发送SYN+ACK包到客户器,进入SYN_RECV状态
3.三次握手
C => [ack] => S
ACK=1,seq=x+1,ack=y+1
客户端收到SYN+ACK包,向服务端发送ACK确认包**(ack=k+1)**
服务端与客户端进入ESTABLISHED状态
如果此时丢包,服务端会不断重试直至超时
⚠️针对SYN Flood的防护措施
SYN队列满后,通过tcp_syncookies参数回发SYN Cookie
若正常连接则客户端会回发SYNCookie
建立连接后,客户端出现故障时
⚠️保活机制
配置了保活的一端会发送保活探测报文,若未接收到则继续发送
尝试次数达到保活探测数仍未收到响应则中断连接
四次挥手
目的:终止连接,TCP四次挥手的流程如下:
客户端或服务端都可发起主动关闭
假设客户端发起关闭
1.第一次挥手
C => [syn/FIN] => S
FIN=1,seq=u
客户端发送FIN/SYN包**(FIN=1,syn=u**(由服务器传送来的最后一次数据的最后一个序号+1)**)**到服务器
客户端进入FIN-WAIT-1
2.第二次挥手
服务器发送SYN+ACK包,syn=v,ack=u+1
C <= [syn/ack] <= S
ACK=1,seq=v,ack=u+1(与SYN相同,一个FIN占用一个序号)
服务器通知上层应用进程:客户端已经没有数据要发送了
服务器进入半关闭CLOCE-WAIT状态
客户端进入FIN-WAIT-2状态,等待服务器发送第三次挥手报文
此时可能还有正在发送中的数据
3.第三次挥手
服务器发送完数据后,发送第三次挥手报文:
C <= [syn/ack/FIN] <= S
FIN=1,ack=u+1,seq=w(可能已经发送了数据)
服务端进入LAST-ACK状态
客户端接受到报文后进入TIME-WAIT
4.第四次挥手
C => [syn/ack] => S
seq=u+1,ack=w+1
服务端CLOSED
客户端等待2MSL时间CLOCED
MSL最长报文段寿命
为什么要有TIME_WAIT?
1.确保有足够时间让对方收到ACK包
如果被动关闭端没有收到ACK就会重发FIN包,一来一回即为2*TIME_WAIT2.避免新旧连接混淆
有些路由器会缓存IP数据包,延迟收到的包可能会与新连接混淆
为什么要四次挥手才能断开连接
因为全双工,发送方和接收方都需要FIN报文和ACK报文
服务器出现大量CLOSE_WAIT状态的原因
对方关闭socket连接,我方忙于读写操作,没有及时关闭连接
1.检查代码,释放资源的代码
2.检查配置,特别是处理请求的线程配置